import mxgraph from './index';
import Base64 from './Base64';
import {Graph} from './Graph';
import {ChangePageSetup} from './EditorUi';
import EditorUi from './EditorUi'
import axios from 'axios';

const {
  mxGraph,
  mxVertexHandler,
  mxConstants,
  mxCellState,
  mxPerimeter,
  mxCellEditor,
  mxGraphHandler,
  mxEvent,
  mxEdgeHandler,
  mxShape,
  mxConnectionConstraint,
  mxPoint,
  mxEventObject,
  mxCodec,
  mxObjectCodec,
  mxUtils,
  mxUrlConverter,
  mxImageExport,
  mxXmlCanvas2D,
  mxCodecRegistry,
  mxResources,
  mxEventSource,
  mxClient,
  mxPopupMenu,
  mxGraphView,
  mxConnectionHandler,
  mxPopupMenuHandler,
  mxUndoManager,
  mxRectangle,
  mxRectangleShape,
  mxPolyline,
  mxPrintPreview,
} = mxgraph;

var setInterval_save = null; //全局定时器
/**
 * Copyright (c) 2006-2012, JGraph Ltd
 */
/**
 * Editor constructor executed on page load.
 */
const Editor = function (chromeless, themes, model, graph, editable) {
  mxEventSource.call(this);
  this.chromeless = (chromeless != null) ? chromeless : this.chromeless;
  this.initStencilRegistry();
  this.graph = graph || this.createGraph(themes, model);
  this.editable = (editable != null) ? editable : !chromeless;
  this.undoManager = this.createUndoManager();
  this.status = '';

  this.getOrCreateFilename = function () {
    //大图默认的名称:之前的
    // return this.filename || mxResources.get('drawing', [Editor.pageCounter]) + '.xml';
    //新的
    return this.filename;
  };

  this.getFilename = function () {
    return this.filename;
  };

  // Sets the status and fires a statusChanged event
  this.setStatus = function (value) {
    this.status = value;
    this.fireEvent(new mxEventObject('statusChanged'));
  };

  // Returns the current status
  this.getStatus = function () {
    return this.status;
  };

  // Updates modified state if graph changes
  this.graphChangeListener = function (sender, eventObject) {
    var edit = (eventObject != null) ? eventObject.getProperty('edit') : null;
    if (edit == null || !edit.ignoreEdit) {
      this.setModified(true);
    }
  };

  this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function () {
    this.graphChangeListener.apply(this, arguments);
  }));
  // Sets persistent graph state defaults
  this.graph.resetViewOnRootChange = false;
  this.init();
};

  /**
   * Counts open editor tabs (must be global for cross-window access)
   */
  Editor.pageCounter = 0;

	/**
	 * Default value for the CSV import dialog.
	 */
	Editor.defaultCsvValue = '##\n' +
		'## Example CSV import. Use ## for comments and # for configuration. Paste CSV below.\n' +
		'## The following names are reserved and should not be used (or ignored):\n' +
		'## id, tooltip, placeholder(s), link and label (see below)\n' +
		'##\n' +
		'#\n' +
		'## Node label with placeholders and HTML.\n' +
		'## Default is \'%name_of_first_column%\'.\n' +
		'#\n' +
		'# label: %name%<br><i style="color:gray;">%position%</i><br><a href="mailto:%email%">Email</a>\n' +
		'#\n' +
		'## Node style (placeholders are replaced once).\n' +
		'## Default is the current style for nodes.\n' +
		'#\n' +
		'# style: label;image=%image%;whiteSpace=wrap;html=1;rounded=1;fillColor=%fill%;strokeColor=%stroke%;\n' +
		'#\n' +
		'## Parent style for nodes with child nodes (placeholders are replaced once).\n' +
		'#\n' +
		'# parentstyle: swimlane;whiteSpace=wrap;html=1;childLayout=stackLayout;horizontal=1;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;\n' +
		'#\n' +
		'## Optional column name that contains a reference to a named style in styles.\n' +
		'## Default is the current style for nodes.\n' +
		'#\n' +
		'# stylename: -\n' +
		'#\n' +
		'## JSON for named styles of the form {"name": "style", "name": "style"} where style is a cell style with\n' +
		'## placeholders that are replaced once.\n' +
		'#\n' +
		'# styles: -\n' +
		'#\n' +
		'## Optional column name that contains a reference to a named label in labels.\n' +
		'## Default is the current label.\n' +
		'#\n' +
		'# labelname: -\n' +
		'#\n' +
		'## JSON for named labels of the form {"name": "label", "name": "label"} where label is a cell label with\n' +
		'## placeholders.\n' +
		'#\n' +
		'# labels: -\n' +
		'#\n' +
		'## Uses the given column name as the identity for cells (updates existing cells).\n' +
		'## Default is no identity (empty value or -).\n' +
		'#\n' +
		'# identity: -\n' +
		'#\n' +
		'## Uses the given column name as the parent reference for cells. Default is no parent (empty or -).\n' +
		'## The identity above is used for resolving the reference so it must be specified.\n' +
		'#\n' +
		'# parent: -\n' +
		'#\n' +
		'## Adds a prefix to the identity of cells to make sure they do not collide with existing cells (whose\n' +
		'## IDs are numbers from 0..n, sometimes with a GUID prefix in the context of realtime collaboration).\n' +
		'## Default is csvimport-.\n' +
		'#\n' +
		'# namespace: csvimport-\n' +
		'#\n' +
		'## Connections between rows ("from": source colum, "to": target column).\n' +
		'## Label, style and invert are optional. Defaults are \'\', current style and false.\n' +
		'## If placeholders are used in the style, they are replaced with data from the source.\n' +
		'## An optional placeholders can be set to target to use data from the target instead.\n' +
		'## In addition to label, an optional fromlabel and tolabel can be used to name the column\n' +
		'## that contains the text for the label in the edges source or target (invert ignored).\n' +
		'## The label is concatenated in the form fromlabel + label + tolabel if all are defined.\n' +
		'## Additional labels can be added by using an optional labels array with entries of the\n' +
		'## form {"label": string, "x": number, "y": number, "dx": number, "dy": number} where\n' +
		'## x is from -1 to 1 along the edge, y is orthogonal, and dx/dy are offsets in pixels.\n' +
		'## The target column may contain a comma-separated list of values.\n' +
		'## Multiple connect entries are allowed.\n' +
		'#\n' +
		'# connect: {"from": "manager", "to": "name", "invert": true, "label": "manages", \\\n' +
		'#          "style": "curved=1;endArrow=blockThin;endFill=1;fontSize=11;"}\n' +
		'# connect: {"from": "refs", "to": "id", "style": "curved=1;fontSize=11;"}\n' +
		'#\n' +
		'## Node x-coordinate. Possible value is a column name. Default is empty. Layouts will\n' +
		'## override this value.\n' +
		'#\n' +
		'# left: \n' +
		'#\n' +
		'## Node y-coordinate. Possible value is a column name. Default is empty. Layouts will\n' +
		'## override this value.\n' +
		'#\n' +
		'# top: \n' +
		'#\n' +
		'## Node width. Possible value is a number (in px), auto or an @ sign followed by a column\n' +
		'## name that contains the value for the width. Default is auto.\n' +
		'#\n' +
		'# width: auto\n' +
		'#\n' +
		'## Node height. Possible value is a number (in px), auto or an @ sign followed by a column\n' +
		'## name that contains the value for the height. Default is auto.\n' +
		'#\n' +
		'# height: auto\n' +
		'#\n' +
		'## Padding for autosize. Default is 0.\n' +
		'#\n' +
		'# padding: -12\n' +
		'#\n' +
		'## Comma-separated list of ignored columns for metadata. (These can be\n' +
		'## used for connections and styles but will not be added as metadata.)\n' +
		'#\n' +
		'# ignore: id,image,fill,stroke,refs,manager\n' +
		'#\n' +
		'## Column to be renamed to link attribute (used as link).\n' +
		'#\n' +
		'# link: url\n' +
		'#\n' +
		'## Spacing between nodes. Default is 40.\n' +
		'#\n' +
		'# nodespacing: 40\n' +
		'#\n' +
		'## Spacing between levels of hierarchical layouts. Default is 100.\n' +
		'#\n' +
		'# levelspacing: 100\n' +
		'#\n' +
		'## Spacing between parallel edges. Default is 40. Use 0 to disable.\n' +
		'#\n' +
		'# edgespacing: 40\n' +
		'#\n' +
		'## Name or JSON of layout. Possible values are auto, none, verticaltree, horizontaltree,\n' +
		'## verticalflow, horizontalflow, organic, circle or a JSON string as used in Layout, Apply.\n' +
		'## Default is auto.\n' +
		'#\n' +
		'# layout: auto\n' +
		'#\n' +
		'## ---- CSV below this line. First line are column names. ----\n' +
		'name,position,id,location,manager,email,fill,stroke,refs,url,image\n' +
		'Evan Miller,CFO,emi,Office 1,,me@example.com,#dae8fc,#6c8ebf,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-9-2-128.png\n' +
		'Edward Morrison,Brand Manager,emo,Office 2,Evan Miller,me@example.com,#d5e8d4,#82b366,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-10-3-128.png\n' +
		'Ron Donovan,System Admin,rdo,Office 3,Evan Miller,me@example.com,#d5e8d4,#82b366,"emo,tva",https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-2-128.png\n' +
		'Tessa Valet,HR Director,tva,Office 4,Evan Miller,me@example.com,#d5e8d4,#82b366,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-3-128.png\n';



// Cross-domain window access is not allowed in FF, so if we
// were opened from another domain then this will fail.
(function () {
  try {
    var op = window;
    while (op.opener != null && typeof op.opener.Editor !== 'undefined' &&
      !isNaN(op.opener.Editor.pageCounter) &&
      // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795
      op.opener != op) {
      op = op.opener;
    }
    // Increments the counter in the first opener in the chain
    if (op != null) {
      op.Editor.pageCounter++;
      Editor.pageCounter = op.Editor.pageCounter;
    }
  } catch (e) {
    // ignore
  }
})();



/**
 * Default value for custom libraries in mxSettings.
 */
Editor.defaultCustomLibraries = [];

/**
 * Capability check for canvas export
 */
Editor.prototype.useCanvasForExport = false;

try {
  var canvas = document.createElement('canvas');
  var img = new Image();

  // LATER: Capability check should not be async
  img.onload = function () {
    try {
      var ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0);

      // Works in Chrome, Firefox, Edge, Safari and Opera
      var result = canvas.toDataURL('image/png');
      Editor.prototype.useCanvasForExport = result != null && result.length > 6;
    } catch (e) {
      // ignore
    }
  };

  // Checks if SVG with foreignObject can be exported
  // 检查是否可以导出带有foreignObject的SVG
  var svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1px" height="1px" version="1.1"><foreignObject pointer-events="all" width="1" height="1"><div xmlns="http://www.w3.org/1999/xhtml"></div></foreignObject></svg>';
  img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
} catch (e) {
  // ignore
}

/**
 * Specifies if local storage should be used (eg. on the iPad which has no filesystem)
 */
Editor.useLocalStorage = typeof (Storage) != 'undefined' && mxClient.IS_IOS;

/**
 * 
 */
Editor.moveImage = (mxClient.IS_SVG) ? '' :
  IMAGE_PATH + '/move.png';

/**
 * 
 */
Editor.rowMoveImage = (mxClient.IS_SVG) ? '' :
  IMAGE_PATH + '/thumb_horz.png';

/**
 * Images below are for lightbox and embedding toolbars.
 */
Editor.helpImage = (mxClient.IS_SVG) ? '' :
  IMAGE_PATH + '/help.png';

/**
 * Sets the default font size.
 */
Editor.checkmarkImage = (mxClient.IS_SVG) ? '' :
  IMAGE_PATH + '/checkmark.gif';

/**
 * Images below are for lightbox and embedding toolbars.
 */
Editor.maximizeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.zoomOutImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.zoomInImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.zoomFitImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.layersImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.previousImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.nextImage = '';

/**
 * 正在保存图片
 */
Editor.spinImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/spin.gif' : '';

/**
 * Add 素材库的 + 图片
 */
Editor.plusImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/plus.png' : '';

/**
 * Used in the GraphViewer lightbox.
 */
Editor.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/delete.png' : '';

/**
 * Specifies the image URL to be used for the transparent background.
 * Edit 素材库的 编辑 图片
 */
Editor.editImage = (mxClient.IS_SVG) ? '' : IMAGE_PATH + '/edit.gif';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.zoomOutLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.zoomInLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.actualSizeLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.printLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.layersLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.closeLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.editLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.previousLargeImage = '';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.nextLargeImage = '';

/**
 * Specifies the image to be used for the refresh button.
 */
Editor.refreshLargeImage = '';

/**
 * Specifies the image to be used for the back button.
 */
Editor.backLargeImage = '';

/**
 * Specifies the image to be used for the back button.
 */
Editor.fullscreenLargeImage = '';

/**
 * All fill styles supported by rough.js.
 */
Editor.roughFillStyles = [{
		val: 'auto',
		dispName: 'Auto'
	}, {
		val: 'hachure',
		dispName: 'Hachure'
	}, {
		val: 'solid',
		dispName: 'Solid'
	},
	{
		val: 'zigzag',
		dispName: 'ZigZag'
	}, {
		val: 'cross-hatch',
		dispName: 'Cross Hatch'
	}, {
		val: 'dots',
		dispName: 'Dots'
	},
	{
		val: 'dashed',
		dispName: 'Dashed'
	}, {
		val: 'zigzag-line',
		dispName: 'ZigZag Line'
	}
];

/**
 * Graph themes for the format panel.
 */
Editor.themes = null;

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.ctrlKey = (mxClient.IS_MAC) ? 'Cmd' : 'Ctrl';

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.hintOffset = 20;

/**
 * Specifies if the diagram should be saved automatically if possible. Default
 * is true.
 */
Editor.popupsAllowed = true;

/**
 * Editor inherits from mxEventSource
 */
mxUtils.extend(Editor, mxEventSource);

/**
 * Stores initial state of mxClient.NO_FO.
 */
Editor.prototype.originalNoForeignObject = mxClient.NO_FO;

/**
 * Specifies the image URL to be used for the transparent background.
 */
Editor.prototype.transparentImage = (mxClient.IS_SVG) ? '' :
  IMAGE_PATH + '/transparent.gif';
/**
 * Blank 1x1 pixel transparent PNG image.
 */
Editor.blankImage = '';

/**
 * Blank 1x1 pixel transparent PNG image.
 */
Editor.hiResImage = (mxClient.IS_SVG) ? '' : IMAGE_PATH + '/img-hi-res.png';

/**
 * Blank 1x1 pixel transparent PNG image.
 */
Editor.loResImage = (mxClient.IS_SVG) ? '' : IMAGE_PATH + '/img-lo-res.png';

/**
 * Specifies if the canvas should be extended in all directions. Default is true.
 */
Editor.prototype.extendCanvas = true;

/**
 * Specifies if the app should run in chromeless mode. Default is false.
 * This default is only used if the contructor argument is null.
 */
Editor.prototype.chromeless = false;

/**
 * Specifies the order of OK/Cancel buttons in dialogs. Default is true.
 * Cancel first is used on Macs, Windows/Confluence uses cancel last.
 */
Editor.prototype.cancelFirst = true;

/**
 * Specifies if the editor is enabled. Default is true.
 */
Editor.prototype.enabled = true;

/**
 * Contains the name which was used for the last save. Default value is null.
 */
Editor.prototype.filename = null;

/**
 * Contains the current modified state of the diagram. This is false for
 * new diagrams and after the diagram was saved.包含关系图的当前修改状态,大图当前如果被修改了，状态为:true;
 */
Editor.prototype.modified = false;

/**
 * Specifies if the diagram should be saved automatically if possible. Default
 * is true.自动保存
 */
Editor.prototype.autosave = true;

/**
 * Specifies the top spacing for the initial page view. Default is 0.
 */
Editor.prototype.initialTopSpacing = 0;

/**
 * Specifies the app name. Default is document.title.
 */
Editor.prototype.appName = document.title;

/**
 * 
 */
Editor.prototype.editBlankUrl = window.location.protocol + '//' + window.location.host + '/';

/**
 * Default value for the graph container overflow style.
 */
Editor.prototype.defaultGraphOverflow = 'hidden';

/**
 * Initializes the environment.
 */
Editor.prototype.init = function () {};

/**
 * Sets the XML node for the current diagram.
 */
Editor.prototype.isChromelessView = function () {
  return this.chromeless;
};

/**
 * Sets the XML node for the current diagram.自动保存xml文件
 */
Editor.prototype.setAutosave = function (value) {
  this.autosave = value;
  this.fireEvent(new mxEventObject('autosaveChanged'));
};

/**
 * 
 */
Editor.prototype.getEditBlankUrl = function (params) {
  return this.editBlankUrl + params;
}

/**
 * 
 */
Editor.prototype.editAsNew = function (xml, title) {
  var p = (title != null) ? '?title=' + encodeURIComponent(title) : '';
  if (urlParams['ui'] != null) {
    p += ((p.length > 0) ? '&' : '?') + 'ui=' + urlParams['ui'];
  }

	if (typeof window.postMessage !== 'undefined' &&
		(document.documentMode == null ||
			document.documentMode >= 10)) {
		var wnd = null;

		var l = mxUtils.bind(this, function (evt) {
			if (evt.data == 'ready' && evt.source == wnd) {
				mxEvent.removeListener(window, 'message', l);
				wnd.postMessage(xml, '*');
			}
		});

		mxEvent.addListener(window, 'message', l);
		wnd = this.graph.openLink(this.getEditBlankUrl(
			p + ((p.length > 0) ? '&' : '?') +
			'client=1'), null, true);
	} else {
		this.graph.openLink(this.getEditBlankUrl(p) +
			'#R' + encodeURIComponent(xml));
	}
};

/**
 * Sets the XML node for the current diagram.
 */
Editor.prototype.createGraph = function (themes, model) {
  var graph = new Graph(null, model, null, null, themes);
  graph.transparentBackground = false;
  // Opens all links in a new window while editing
  if (!this.chromeless) {
    graph.isBlankLink = function (href) {
      return !this.isExternalProtocol(href);
    };
  }
  return graph;
};

/**
 * Sets the XML node for the current diagram.
 */
Editor.prototype.resetGraph = function () {
  this.graph.gridEnabled = !this.isChromelessView() || urlParams['grid'] == '1';
  this.graph.graphHandler.guidesEnabled = true;
  this.graph.setTooltips(true); // 是否显示提示,默认显示Cell的名称 
  this.graph.setConnectable(true); // 是否允许Cells通过其中部的连接点新建连接,false则通过连接线连接
  this.graph.foldingEnabled = true;
  this.graph.scrollbars = this.graph.defaultScrollbars;
  this.graph.pageVisible = this.graph.defaultPageVisible;
  this.graph.pageBreaksVisible = this.graph.pageVisible;
  this.graph.preferPageSize = this.graph.pageBreaksVisible;
  this.graph.background = null;
  this.graph.pageScale = mxGraph.prototype.pageScale;
  this.graph.pageFormat = mxGraph.prototype.pageFormat;
  this.graph.currentScale = 1;
  this.graph.currentTranslate.x = 0;
  this.graph.currentTranslate.y = 0;
  this.updateGraphComponents();
  this.graph.view.setScale(1);
};

/**
 * Sets the XML node for the current diagram.
 */
Editor.prototype.readGraphState = function (node) {
  this.graph.gridEnabled = node.getAttribute('grid') != '0' && (!this.isChromelessView() || urlParams['grid'] == '1');
  this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize;
  this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0';
  this.graph.setTooltips(node.getAttribute('tooltips') != '0');
  this.graph.setConnectable(node.getAttribute('connect') != '0');
  this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0';
  this.graph.foldingEnabled = node.getAttribute('fold') != '0';

  if (this.isChromelessView() && this.graph.foldingEnabled) {
    this.graph.foldingEnabled = urlParams['nav'] == '1';
    this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled;
  }

  var ps = parseFloat(node.getAttribute('pageScale'));
  if (!isNaN(ps) && ps > 0) {
    this.graph.pageScale = ps;
  } else {
    this.graph.pageScale = mxGraph.prototype.pageScale;
  }

	if (!this.graph.isLightboxView() && !this.graph.isViewer()) {
		var pv = node.getAttribute('page');

    if (pv != null) {
      this.graph.pageVisible = (pv != '0');
    } else {
      this.graph.pageVisible = this.graph.defaultPageVisible;
    }
  } else {
    this.graph.pageVisible = false;
  }

  this.graph.pageBreaksVisible = this.graph.pageVisible;
  this.graph.preferPageSize = this.graph.pageBreaksVisible;

  var pw = parseFloat(node.getAttribute('pageWidth'));
  var ph = parseFloat(node.getAttribute('pageHeight'));

  if (!isNaN(pw) && !isNaN(ph)) {
    this.graph.pageFormat = new mxRectangle(0, 0, pw, ph);
  }

  // Loads the persistent state settings
  var bg = node.getAttribute('background');
  if (bg != null && bg.length > 0) {
    this.graph.background = bg;
  } else {
    this.graph.background = null;
  }
};

/**
 * Sets the XML node for the current diagram.
 */
Editor.prototype.setGraphXml = function (node) {
  if (node != null) {
    var dec = new mxCodec(node.ownerDocument);
    if (node.nodeName == 'mxGraphModel') {
      this.graph.model.beginUpdate();
      try {
        this.graph.model.clear();
        this.graph.view.scale = 1;
        this.readGraphState(node);
        this.updateGraphComponents();
        dec.decode(node, this.graph.getModel());
      } finally {
        this.graph.model.endUpdate();
      }

      this.fireEvent(new mxEventObject('resetGraphView'));
    } else if (node.nodeName == 'root') {
      this.resetGraph();
      // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml
      var wrapper = dec.document.createElement('mxGraphModel');
      wrapper.appendChild(node);

      dec.decode(wrapper, this.graph.getModel());
      this.updateGraphComponents();
      this.fireEvent(new mxEventObject('resetGraphView'));
    } else {
      throw {
        message: mxResources.get('cannotOpenFile'),
        node: node,
        toString: function () {
          return this.message;
        }
      };
    }
  } else {
    this.resetGraph();
    this.graph.model.clear();
    this.fireEvent(new mxEventObject('resetGraphView'));
  }
};

/**
 * Returns the XML node that represents the current diagram.
 */
Editor.prototype.getGraphXml = function (ignoreSelection) {
  ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
  var node = null;
  if (ignoreSelection) {
    var enc = new mxCodec(mxUtils.createXmlDocument());
    node = enc.encode(this.graph.getModel());
  } else {
    node = this.graph.encodeCells(mxUtils.sortCells(this.graph.model.getTopmostCells(
      this.graph.getSelectionCells())));
  }

  if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0) {
    node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100);
    node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100);
  }

  node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0');
  node.setAttribute('gridSize', this.graph.gridSize);
  node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0');
  node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0');
  node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0');
  node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0');
  node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0');
  node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0');
  node.setAttribute('pageScale', this.graph.pageScale);
  node.setAttribute('pageWidth', this.graph.pageFormat.width);
  node.setAttribute('pageHeight', this.graph.pageFormat.height);

  if (this.graph.background != null) {
    node.setAttribute('background', this.graph.background);
  }
  // console.log("node:",node)
  return node;
};

/**
 * Keeps the graph container in sync with the persistent graph state
 */
Editor.prototype.updateGraphComponents = function () {
  var graph = this.graph;
  if (graph.container != null) {
    graph.view.validateBackground();
    graph.container.style.overflow = (graph.scrollbars) ? 'auto' : this.defaultGraphOverflow;
    this.fireEvent(new mxEventObject('updateGraphComponents'));
  }
};

/**
 * Sets the modified flag.
 */
Editor.prototype.setModified = function (value) {
  // console.log("页面修改就执行:", value);
  //页面修改就执行 value 就为:true;
  if (value) {
    this.setStatus(mxUtils.htmlEntities(mxResources.get('modificationNotSavedClickHereToSave')));
  } else {
    this.setStatus(mxUtils.htmlEntities(mxResources.get('allChangesSaved')));
  }
  this.modified = value;
};

/**
 * 每隔20s后执行一次
 */
Editor.prototype.setTimeSaveXML = function () {
  // clearInterval(setInterval_save); //每次进来先清除定时器
  // setInterval_save = setInterval(() => {
  // }, 20000)



  //当前画布里面的数据转为xml
  var xml = mxUtils.getXml(this.getGraphXml());
  // console.log("xmls:",xml);
  // console.log('当前导航栏的状态：', this.status);
  // console.log('自动保存传参的值：',this.filename,this._id,xml,this.status);
  var obj = {
    id: this._id,
    name: this.filename,
    xml: xml,
  };
  if (this._id != null) {
    // 如果[修改未保存]文字存在才调下面接口
    if (this.status == '修改未保存,点击此处保存.') {
      axios.put(SAVE_URL, {
        id: obj.id,
        name: obj.name,
        xml: obj.xml
      }).then((res) => {
        // console.log("自动保存：", obj.xml);
        if (res.status == '200') {
          // console.log("保存成功put：", res.data);
          //更新导航栏显示地方的文字
          this.setStatus(mxUtils.htmlEntities(mxResources.get('allChangesSaved')));
        }
      }).catch(error => {
        console.error(error);
      })
    } else {
      //
      return false;
    }
  } else {
    //提示信息
    // mxUtils.confirm('画布内容已被修改,请手动去保存,如果不保存，继续执行，数据将会丢失!');
    return false;
  }

  //设置倒计时，每隔20s 自动保存一次
}
/**
 * Sets the filename.
 */
Editor.prototype.setFilename = function (value) {
  this.filename = value;
};

/**
 * Creates and returns a new undo manager.
 */
Editor.prototype.createUndoManager = function () {
  var graph = this.graph;
  var undoMgr = new mxUndoManager();

  this.undoListener = function (sender, evt) {
    undoMgr.undoableEditHappened(evt.getProperty('edit'));
  };

  // Installs the command history
  var listener = mxUtils.bind(this, function (sender, evt) {
    this.undoListener.apply(this, arguments);
  });

  graph.getModel().addListener(mxEvent.UNDO, listener);
  graph.getView().addListener(mxEvent.UNDO, listener);

	// Keeps the selection in sync with the history
	var undoHandler = function (sender, evt) {
		var cand = graph.getSelectionCellsForChanges(evt.getProperty('edit').changes, function (change) {
			// Only selects changes to the cell hierarchy
			return !(change instanceof mxChildChange);
		});

		if (cand.length > 0) {
			var model = graph.getModel();
			var cells = [];

			for (var i = 0; i < cand.length; i++) {
				if (graph.view.getState(cand[i]) != null) {
					cells.push(cand[i]);
				}
			}

			graph.setSelectionCells(cells);
		}
	};

	undoMgr.addListener(mxEvent.UNDO, undoHandler);
	undoMgr.addListener(mxEvent.REDO, undoHandler);

	return undoMgr;
};

/**
 * Adds basic stencil set (no namespace).
 */
Editor.prototype.initStencilRegistry = function () {};

/**
 * Creates and returns a new undo manager.
 */
Editor.prototype.destroy = function () {
  if (this.graph != null) {
    this.graph.destroy();
    this.graph = null;
  }
};

/**
 * Class for asynchronously opening a new window and loading a file at the same
 * time. This acts as a bridge between the open dialog and the new editor.
 */
const OpenFile = function (done) {
  this.producer = null;
  this.consumer = null;
  this.done = done;
  this.args = null;
};

/**
 * Registers the editor from the new window.
 */
OpenFile.prototype.setConsumer = function (value) {
  this.consumer = value;
  this.execute();
};

/**
 * Sets the data from the loaded file.
 */
OpenFile.prototype.setData = function () {
  this.args = arguments;
  this.execute();
};

/**
 * Displays an error message.
 */
OpenFile.prototype.error = function (msg) {
  this.cancel(true);
  mxUtils.alert(msg);
};

/**
 * Consumes the data.
 */
OpenFile.prototype.execute = function () {
  console.log('执行了: ', this.consumer, this.args);
  if (this.consumer != null && this.args != null) {
    this.cancel(false);
    this.consumer.apply(this, this.args);
  }
};

/**
 * Cancels the operation.
 */
OpenFile.prototype.cancel = function (cancel) {
  if (this.done != null) {
    this.done((cancel != null) ? cancel : true);
  }
};

/**
 * Basic dialogs that are available in the viewer (print dialog).
 */
function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll, transparent, onResize, ignoreBgClick) {
  var dx = 0;

  if (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) {
    // Adds padding as a workaround for box model in older IE versions
    // This needs to match the total padding of geDialog in CSS
    dx = 80;
  }

  w += dx;
  h += dx;

  var w0 = w;
  var h0 = h;

	var ds = mxUtils.getDocumentSize();

	// Workaround for print dialog offset in viewer lightbox
	if (window.innerHeight != null) {
		ds.height = window.innerHeight;
	}

	var dh = ds.height;
	var left = Math.max(1, Math.round((ds.width - w - 64) / 2));
	var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));

  // Keeps window size inside available space
  if (!mxClient.IS_QUIRKS) {
    elt.style.maxHeight = '100%';
  }

  w = (document.body != null) ? Math.min(w, document.body.scrollWidth - 64) : w;
  h = Math.min(h, dh - 64);

  // Increments zIndex to put subdialogs and background over existing dialogs and background
  if (editorUi.dialogs.length > 0) {
    this.zIndex += editorUi.dialogs.length * 2;
  }

  if (this.bg == null) {
    this.bg = editorUi.createDiv('background');
    this.bg.style.position = 'absolute';
    this.bg.style.background = Dialog.backdropColor;
    this.bg.style.height = dh + 'px';
    this.bg.style.right = '0px';
    this.bg.style.zIndex = this.zIndex - 2;

    mxUtils.setOpacity(this.bg, this.bgOpacity);

    if (mxClient.IS_QUIRKS) {
      new mxDivResizer(this.bg);
    }
  }

  var origin = mxUtils.getDocumentScrollOrigin(document);
  this.bg.style.left = origin.x + 'px';
  this.bg.style.top = origin.y + 'px';
  left += origin.x;
  top += origin.y;

  if (modal) {
    document.body.appendChild(this.bg);
  }

  var div = editorUi.createDiv(transparent ? 'geTransDialog' : 'geDialog');
  var pos = this.getPosition(left, top, w, h);
  left = pos.x;
  top = pos.y;
  // 全局 dialog 的样式
  div.style.width = (w + 60) + 'px';
  div.style.minHeight = (h + 60) + 'px'; //保存xml时，弹层的高度
  div.style.left = (left + 0) + 'px';
  div.style.top = top + 'px';
  div.style.zIndex = this.zIndex;

  div.appendChild(elt);
  document.body.appendChild(div);

  // Adds vertical scrollbars if needed
  if (!noScroll && elt.clientHeight > div.clientHeight - 64) {
    elt.style.overflowY = 'auto';
  }

  if (closable) {
    var img = document.createElement('img');
    img.setAttribute('src', Dialog.prototype.closeImage);
    img.setAttribute('title', mxResources.get('close'));
    img.className = 'geDialogClose';
    img.style.top = (top + 14) + 'px';
    img.style.left = (left + w + 38 - dx) + 'px';
    img.style.zIndex = this.zIndex;

    mxEvent.addListener(img, 'click', mxUtils.bind(this, function () {
      editorUi.hideDialog(true);
    }));

    document.body.appendChild(img);
    this.dialogImg = img;

		if (!ignoreBgClick) {
			var mouseDownSeen = false;

			mxEvent.addGestureListeners(this.bg, mxUtils.bind(this, function (evt) {
				mouseDownSeen = true;
			}), null, mxUtils.bind(this, function (evt) {
				if (mouseDownSeen) {
					editorUi.hideDialog(true);
					mouseDownSeen = false;
				}
			}));
		}
	}

  this.resizeListener = mxUtils.bind(this, function () {
    if (onResize != null) {
      var newWH = onResize();
      if (newWH != null) {
        w0 = w = newWH.w;
        h0 = h = newWH.h;
      }
    }

    var ds = mxUtils.getDocumentSize();
    dh = ds.height;
    this.bg.style.height = dh + 'px';

    left = Math.max(1, Math.round((ds.width - w - 64) / 2));
    top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));
    w = (document.body != null) ? Math.min(w0, document.body.scrollWidth - 64) : w0;
    h = Math.min(h0, dh - 64);

    var pos = this.getPosition(left, top, w, h);
    left = pos.x;
    top = pos.y;

    div.style.left = left + 'px';
    div.style.top = top + 'px';
    div.style.width = w + 'px';
    div.style.height = h + 'px';

    // Adds vertical scrollbars if needed
    if (!noScroll && elt.clientHeight > div.clientHeight - 64) {
      elt.style.overflowY = 'auto';
    }

    if (this.dialogImg != null) {
      this.dialogImg.style.top = (top + 14) + 'px';
      this.dialogImg.style.left = (left + w + 38 - dx) + 'px';
    }
  });

  mxEvent.addListener(window, 'resize', this.resizeListener);
  this.onDialogClose = onClose;
  this.container = div;
  editorUi.editor.fireEvent(new mxEventObject('showDialog'));
};

/**
 * 
 */
Dialog.backdropColor = '#01122E';

/**
 * 
 */
Dialog.prototype.zIndex = mxPopupMenu.prototype.zIndex - 1;

/**
 * 
 */
Dialog.prototype.noColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/nocolor.png' : '';

/**
 * 关闭图片
 */
Dialog.prototype.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/close.png' : '';

/**
 * 
 */
Dialog.prototype.clearImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/clear.gif' : '';

/**
 * 
 */
Dialog.prototype.lockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/locked.png' : '';

/**
 * 
 */
Dialog.prototype.unlockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/unlocked.png' : '';

/**
 * Removes the dialog from the DOM.
 */
Dialog.prototype.bgOpacity = 80;

/**
 * Removes the dialog from the DOM.
 */
Dialog.prototype.getPosition = function (left, top) {
  return new mxPoint(left, top);
};

/**
 * Removes the dialog from the DOM.
 */
Dialog.prototype.close = function (cancel, isEsc) {
  if (this.onDialogClose != null) {
    if (this.onDialogClose(cancel, isEsc) == false) {
      return false;
    }
    this.onDialogClose = null;
  }

  if (this.dialogImg != null) {
    this.dialogImg.parentNode.removeChild(this.dialogImg);
    this.dialogImg = null;
  }

  if (this.bg != null && this.bg.parentNode != null) {
    this.bg.parentNode.removeChild(this.bg);
  }

  mxEvent.removeListener(window, 'resize', this.resizeListener);
  this.container.parentNode.removeChild(this.container);
};

/**
 * 
 */
var ErrorDialog = function (editorUi, title, message, buttonText, fn, retry, buttonText2, fn2, hide, buttonText3, fn3) {
  hide = (hide != null) ? hide : true;

  var div = document.createElement('div');
  div.style.textAlign = 'center';

  if (title != null) {
    var hd = document.createElement('div');
    hd.style.padding = '0px';
    hd.style.margin = '0px';
    hd.style.fontSize = '18px';
    hd.style.paddingBottom = '16px';
    hd.style.marginBottom = '10px';
    hd.style.borderBottom = '1px solid #c0c0c0';
    hd.style.color = 'gray';
    hd.style.whiteSpace = 'nowrap';
    hd.style.textOverflow = 'ellipsis';
    hd.style.overflow = 'hidden';
    mxUtils.write(hd, title);
    hd.setAttribute('title', title);
    div.appendChild(hd);
  }

  var p2 = document.createElement('div');
  p2.style.lineHeight = '1.2em';
  p2.style.padding = '6px';
  p2.innerHTML = message;
  div.appendChild(p2);

  var btns = document.createElement('div');
  btns.style.marginTop = '12px';
  btns.style.textAlign = 'center';

  if (retry != null) {
    var retryBtn = mxUtils.button(mxResources.get('tryAgain'), function () {
      editorUi.hideDialog();
      retry();
    });
    retryBtn.className = 'geBtn';
    btns.appendChild(retryBtn);

    btns.style.textAlign = 'center';
  }

  if (buttonText3 != null) {
    var btn3 = mxUtils.button(buttonText3, function () {
      if (fn3 != null) {
        fn3();
      }
    });

    btn3.className = 'geBtn';
    btns.appendChild(btn3);
  }

  var btn = mxUtils.button(buttonText, function () {
    if (hide) {
      editorUi.hideDialog();
    }

    if (fn != null) {
      fn();
    }
  });

  btn.className = 'geBtn';
  btns.appendChild(btn);

  if (buttonText2 != null) {
    var mainBtn = mxUtils.button(buttonText2, function () {
      if (hide) {
        editorUi.hideDialog();
      }

      if (fn2 != null) {
        fn2();
      }
    });

    mainBtn.className = 'geBtn gePrimaryBtn';
    btns.appendChild(mainBtn);
  }

  this.init = function () {
    btn.focus();
  };

  div.appendChild(btns);

  this.container = div;
};

/**
 * Constructs a new print dialog.
 */
var PrintDialog = function (editorUi, title) {
  this.create(editorUi, title);
};

/**
 * Constructs a new print dialog.
 */
PrintDialog.prototype.create = function (editorUi) {
  var graph = editorUi.editor.graph;
  var row, td;

  var table = document.createElement('table');
  table.style.width = '100%';
  table.style.height = '100%';

  var thead = document.createElement('thead');
  var tr = document.createElement('tr');
  var th = document.createElement('th')

  table.appendChild(thead);
  thead.appendChild(tr);
  tr.appendChild(th);
  mxUtils.write(th, '打印'); //加标题'打印'
  th.style.fontSize = '18px';
  th.style.textAlign = 'center';
  th.setAttribute('colspan', '2');

  var tbody = document.createElement('tbody');

  row = document.createElement('tr');

  var onePageCheckBox = document.createElement('input');
  onePageCheckBox.setAttribute('type', 'checkbox');
  td = document.createElement('td');
  td.setAttribute('colspan', '2');
  td.style.fontSize = '10pt';
  td.appendChild(onePageCheckBox);

  var span = document.createElement('span');
  mxUtils.write(span, ' ' + mxResources.get('fitPage'));
  td.appendChild(span);

  mxEvent.addListener(span, 'click', function (evt) {
    onePageCheckBox.checked = !onePageCheckBox.checked;
    pageCountCheckBox.checked = !onePageCheckBox.checked;
    mxEvent.consume(evt);
  });

  mxEvent.addListener(onePageCheckBox, 'change', function () {
    pageCountCheckBox.checked = !onePageCheckBox.checked;
  });

  row.appendChild(td);
  tbody.appendChild(row);

  row = row.cloneNode(false);

  var pageCountCheckBox = document.createElement('input');
  pageCountCheckBox.setAttribute('type', 'checkbox');
  td = document.createElement('td');
  td.style.fontSize = '10pt';
  td.appendChild(pageCountCheckBox);

  var span = document.createElement('span');
  mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':');
  td.appendChild(span);

  mxEvent.addListener(span, 'click', function (evt) {
    pageCountCheckBox.checked = !pageCountCheckBox.checked;
    onePageCheckBox.checked = !pageCountCheckBox.checked;
    mxEvent.consume(evt);
  });

  row.appendChild(td);

  var pageCountInput = document.createElement('input');
  pageCountInput.setAttribute('value', '1');
  pageCountInput.setAttribute('type', 'number');
  pageCountInput.setAttribute('min', '1');
  pageCountInput.setAttribute('size', '4');
  pageCountInput.setAttribute('disabled', 'disabled');
  pageCountInput.style.width = '50px';

  td = document.createElement('td');
  td.style.fontSize = '10pt';
  td.appendChild(pageCountInput);
  mxUtils.write(td, ' ' + mxResources.get('pages') + ' (默认最大值)');
  row.appendChild(td);
  tbody.appendChild(row);

  mxEvent.addListener(pageCountCheckBox, 'change', function () {
    if (pageCountCheckBox.checked) {
      pageCountInput.removeAttribute('disabled');
    } else {
      pageCountInput.setAttribute('disabled', 'disabled');
    }

    onePageCheckBox.checked = !pageCountCheckBox.checked;
  });

  row = row.cloneNode(false);

  td = document.createElement('td');
  mxUtils.write(td, mxResources.get('pageScale') + ':');
  row.appendChild(td);

  td = document.createElement('td');
  var pageScaleInput = document.createElement('input');
  pageScaleInput.setAttribute('value', '100 %');
  pageScaleInput.setAttribute('size', '5');
  pageScaleInput.style.width = '50px';

  td.appendChild(pageScaleInput);
  row.appendChild(td);
  tbody.appendChild(row);

  row = document.createElement('tr');
  td = document.createElement('td');
  td.colSpan = 2;
  td.style.paddingTop = '20px';
  td.setAttribute('align', 'right');

  // Overall scale for print-out to account for print borders in dialogs etc
  function preview(print) {
    var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked;
    var printScale = parseInt(pageScaleInput.value) / 100;

    if (isNaN(printScale)) {
      printScale = 1;
      pageScaleInput.value = '100%';
    }

    // Workaround to match available paper size in actual print output
    printScale *= 0.75;

    var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;
    var scale = 1 / graph.pageScale;

    if (autoOrigin) {
      var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value);

      if (!isNaN(pageCount)) {
        scale = mxUtils.getScaleForPageCount(pageCount, graph, pf);
      }
    }

    // Negative coordinates are cropped or shifted if page visible
    var gb = graph.getGraphBounds();
    var border = 0;
    var x0 = 0;
    var y0 = 0;

    // Applies print scale
    pf = mxRectangle.fromRectangle(pf);
    pf.width = Math.ceil(pf.width * printScale);
    pf.height = Math.ceil(pf.height * printScale);
    scale *= printScale;

    // Starts at first visible page
    if (!autoOrigin && graph.pageVisible) {
      var layout = graph.getPageLayout();
      x0 -= layout.x * pf.width;
      y0 -= layout.y * pf.height;
    } else {
      autoOrigin = true;
    }

    var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin);
    preview.open();

    if (print) {
      PrintDialog.printPreview(preview);
    }
  };

  var cancelBtn = mxUtils.button(mxResources.get('cancel'), function () {
    editorUi.hideDialog();
  });
  cancelBtn.className = 'geBtn';

  if (editorUi.editor.cancelFirst) {
    td.appendChild(cancelBtn);
  }

  if (PrintDialog.previewEnabled) {
    var previewBtn = mxUtils.button(mxResources.get('preview'), function () {
      editorUi.hideDialog();
      preview(false);
    });
    previewBtn.className = 'geBtn';
    td.appendChild(previewBtn);
  }

  var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function () {
    editorUi.hideDialog();
    preview(true);
  });
  printBtn.className = 'geBtn gePrimaryBtn';
  td.appendChild(printBtn);

  if (!editorUi.editor.cancelFirst) {
    td.appendChild(cancelBtn);
  }

  row.appendChild(td);
  tbody.appendChild(row);

  table.appendChild(tbody);
  this.container = table;
};

/**
 * Constructs a new print dialog.
 */
PrintDialog.printPreview = function (preview) {
  try {
    if (preview.wnd != null) {
      var printFn = function () {
        preview.wnd.focus();
        preview.wnd.print();
        preview.wnd.close();
      };

      // Workaround for Google Chrome which needs a bit of a
      // delay in order to render the SVG contents
      // Needs testing in production
      if (mxClient.IS_GC) {
        window.setTimeout(printFn, 500);
      } else {
        printFn();
      }
    }
  } catch (e) {
    // ignores possible Access Denied
  }
};

/**
 * Constructs a new print dialog.
 */
PrintDialog.createPrintPreview = function (graph, scale, pf, border, x0, y0, autoOrigin) {
  var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);
  preview.title = mxResources.get('preview');
  preview.printBackgroundImage = true;
  preview.autoOrigin = autoOrigin;
  var bg = graph.background;

  if (bg == null || bg == '' || bg == mxConstants.NONE) {
    bg = '#ffffff';
  }

  preview.backgroundColor = bg;

  var writeHead = preview.writeHead;

  // Adds a border in the preview
  preview.writeHead = function (doc) {
    writeHead.apply(this, arguments);
    doc.writeln('<style type="text/css">');
    doc.writeln('@media screen {');
    doc.writeln('  body > div { padding:30px;box-sizing:content-box; }');
    doc.writeln('}');
    doc.writeln('</style>');
  };
  return preview;
};

/**
 * Specifies if the preview button should be enabled. Default is true.
 */
PrintDialog.previewEnabled = true;

/**
 * Constructs a new page setup dialog.
 */
var PageSetupDialog = function (editorUi) {
  var graph = editorUi.editor.graph;
  var row, td;

  var table = document.createElement('table');
  table.style.width = '100%';
  table.style.height = '100%';
  var tbody = document.createElement('tbody');

  row = document.createElement('tr');

  td = document.createElement('td');
  td.style.verticalAlign = 'top';
  td.style.fontSize = '10pt';
  mxUtils.write(td, mxResources.get('paperSize') + ':');

  row.appendChild(td);

  td = document.createElement('td');
  td.style.verticalAlign = 'top';
  td.style.fontSize = '10pt';

  var accessor = PageSetupDialog.addPageFormatPanel(td, 'pagesetupdialog', graph.pageFormat);

  row.appendChild(td);
  tbody.appendChild(row);

  row = document.createElement('tr');

  td = document.createElement('td');
  mxUtils.write(td, mxResources.get('background') + ':');

  row.appendChild(td);

  td = document.createElement('td');
  td.style.whiteSpace = 'nowrap';

  var backgroundInput = document.createElement('input');
  backgroundInput.setAttribute('type', 'text');
  var backgroundButton = document.createElement('button');

  backgroundButton.style.width = '18px';
  backgroundButton.style.height = '18px';
  backgroundButton.style.marginRight = '20px';
  backgroundButton.style.backgroundPosition = 'center center';
  backgroundButton.style.display = 'inline-block'; // 背景按钮
  backgroundButton.style.backgroundRepeat = 'no-repeat';

  var newBackgroundColor = graph.background;

  function updateBackgroundColor() {
    if (newBackgroundColor == null || newBackgroundColor == mxConstants.NONE) {
      backgroundButton.style.backgroundColor = '';
      backgroundButton.style.backgroundImage = 'url(\'' + Dialog.prototype.noColorImage + '\')';
    } else {
      backgroundButton.style.backgroundColor = newBackgroundColor;
      backgroundButton.style.backgroundImage = '';
    }
  };

  updateBackgroundColor();

  mxEvent.addListener(backgroundButton, 'click', function (evt) {
    editorUi.pickColor(newBackgroundColor || 'none', function (color) {
      newBackgroundColor = color;
      updateBackgroundColor();
    });
    mxEvent.consume(evt);
  });

  td.appendChild(backgroundButton);

  mxUtils.write(td, mxResources.get('gridSize') + ':');

  var gridSizeInput = document.createElement('input');
  gridSizeInput.setAttribute('type', 'number');
  gridSizeInput.setAttribute('min', '0');
  gridSizeInput.style.width = '40px';
  gridSizeInput.style.marginLeft = '6px';

  gridSizeInput.value = graph.getGridSize();
  td.appendChild(gridSizeInput);

  mxEvent.addListener(gridSizeInput, 'change', function () {
    var value = parseInt(gridSizeInput.value);
    gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value);
  });

  row.appendChild(td);
  tbody.appendChild(row);

  row = document.createElement('tr');
  td = document.createElement('td');

  mxUtils.write(td, mxResources.get('image') + ':');

  row.appendChild(td);
  td = document.createElement('td');

  var changeImageLink = document.createElement('a');
  changeImageLink.style.textDecoration = 'underline';
  changeImageLink.style.cursor = 'pointer';
  changeImageLink.style.color = '#a0a0a0';

  var newBackgroundImage = graph.backgroundImage;

	function updateBackgroundImage() {
		if (newBackgroundImage == null) {
			changeImageLink.removeAttribute('title');
			changeImageLink.style.fontSize = '';
			changeImageLink.innerHTML = mxUtils.htmlEntities(mxResources.get('change')) + '...';
		} else {
			changeImageLink.setAttribute('title', newBackgroundImage.src);
			changeImageLink.style.fontSize = '11px';
			changeImageLink.innerHTML = mxUtils.htmlEntities(newBackgroundImage.src.substring(0, 42)) + '...';
		}
	};

	mxEvent.addListener(changeImageLink, 'click', function (evt) {
		editorUi.showBackgroundImageDialog(function (image, failed) {
			if (!failed) {
				newBackgroundImage = image;
				updateBackgroundImage();
			}
		}, newBackgroundImage);

    mxEvent.consume(evt);
  });

  updateBackgroundImage();

  td.appendChild(changeImageLink);

  row.appendChild(td);
  tbody.appendChild(row);

  row = document.createElement('tr');
  td = document.createElement('td');
  td.colSpan = 2;
  td.style.paddingTop = '16px';
  td.setAttribute('align', 'right');

  var cancelBtn = mxUtils.button(mxResources.get('cancel'), function () {
    editorUi.hideDialog();
  });
  cancelBtn.className = 'geBtn';

  if (editorUi.editor.cancelFirst) {
    td.appendChild(cancelBtn);
  }

	var applyBtn = mxUtils.button(mxResources.get('apply'), function () {
		editorUi.hideDialog();
		var gridSize = parseInt(gridSizeInput.value);

		if (!isNaN(gridSize) && graph.gridSize !== gridSize) {
			graph.setGridSize(gridSize);
		}

    var change = new ChangePageSetup(editorUi, newBackgroundColor,
      newBackgroundImage, accessor.get());
    change.ignoreColor = graph.background == newBackgroundColor;

    var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null;
    var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null;

    change.ignoreImage = oldSrc === newSrc;

    if (graph.pageFormat.width != change.previousFormat.width ||
      graph.pageFormat.height != change.previousFormat.height ||
      !change.ignoreColor || !change.ignoreImage) {
      graph.model.execute(change);
    }
  });
  applyBtn.className = 'geBtn gePrimaryBtn';
  td.appendChild(applyBtn);

  if (!editorUi.editor.cancelFirst) {
    td.appendChild(cancelBtn);
  }

  row.appendChild(td);
  tbody.appendChild(row);

  table.appendChild(tbody);
  this.container = table;
};

/**
 * 
 */
PageSetupDialog.addPageFormatPanel = function (div, namePostfix, pageFormat, pageFormatListener) {
  var formatName = 'format-' + namePostfix;

  var portraitCheckBox = document.createElement('input');
  portraitCheckBox.setAttribute('name', formatName);
  portraitCheckBox.setAttribute('type', 'radio');
  portraitCheckBox.setAttribute('value', 'portrait');

  var landscapeCheckBox = document.createElement('input');
  landscapeCheckBox.setAttribute('name', formatName);
  landscapeCheckBox.setAttribute('type', 'radio');
  landscapeCheckBox.setAttribute('value', 'landscape');

  var paperSizeSelect = document.createElement('select');
  paperSizeSelect.style.marginBottom = '8px';
  paperSizeSelect.style.width = '202px'; // selcet 下拉框的样式
  paperSizeSelect.style.padding = '3px 0px';
  paperSizeSelect.style.border = '1px solid #aaa';
  paperSizeSelect.style.borderRadius = '3px';

  var formatDiv = document.createElement('div');
  // formatDiv.style.marginLeft = '4px';
  formatDiv.style.marginTop = '10px'; //新加的
  formatDiv.style.width = '210px';
  formatDiv.style.height = '25px';
  formatDiv.style.lineHeight = '25px'; // 图表里面 的 页面尺寸的地方， radio 纵向和横向

  portraitCheckBox.style.marginRight = '6px';
  formatDiv.appendChild(portraitCheckBox);

  var portraitSpan = document.createElement('span');
  portraitSpan.style.maxWidth = '100px';
  mxUtils.write(portraitSpan, mxResources.get('portrait'));
  formatDiv.appendChild(portraitSpan);

  landscapeCheckBox.style.marginLeft = '40px'; // 横向 向右的距离
  landscapeCheckBox.style.marginRight = '6px';
  formatDiv.appendChild(landscapeCheckBox);

  var landscapeSpan = document.createElement('span');
  landscapeSpan.style.width = '100px';
  mxUtils.write(landscapeSpan, mxResources.get('landscape'));
  formatDiv.appendChild(landscapeSpan)

  var customDiv = document.createElement('div');
  // customDiv.style.marginLeft = '4px';
  customDiv.style.marginTop = '10px';
  customDiv.style.width = '210px';
  customDiv.style.height = '25px';


  var widthInput = document.createElement('input');
  widthInput.setAttribute('size', '7');
  widthInput.style.textAlign = 'right';
  widthInput.style.borderRadius = '3px'; // 新加的圆角
  widthInput.style.border = '1px solid #aaa'; // 新加的边框

  customDiv.appendChild(widthInput);
  mxUtils.write(customDiv, ' in x ');

  var heightInput = document.createElement('input');
  heightInput.setAttribute('size', '7');
  heightInput.style.textAlign = 'right';
  heightInput.style.borderRadius = '3px'; // 新加的圆角
  heightInput.style.border = '1px solid #aaa'; // 新加的边框
  customDiv.appendChild(heightInput);
  mxUtils.write(customDiv, ' in');

  formatDiv.style.display = 'none';
  customDiv.style.display = 'none';

  var pf = new Object();
  var formats = PageSetupDialog.getFormats();

  for (var i = 0; i < formats.length; i++) {
    var f = formats[i];
    pf[f.key] = f;

    var paperSizeOption = document.createElement('option');
    paperSizeOption.setAttribute('value', f.key);
    mxUtils.write(paperSizeOption, f.title);
    paperSizeSelect.appendChild(paperSizeOption);
  }

  var customSize = false;

  function listener(sender, evt, force) {
    if (force || (widthInput != document.activeElement && heightInput != document.activeElement)) {
      var detected = false;

      for (var i = 0; i < formats.length; i++) {
        var f = formats[i];

        // Special case where custom was chosen
        if (customSize) {
          if (f.key == 'custom') {
            paperSizeSelect.value = f.key;
            customSize = false;
          }
        } else if (f.format != null) {
          // Fixes wrong values for previous A4 and A5 page sizes
          if (f.key == 'a4') {
            if (pageFormat.width == 826) {
              pageFormat = mxRectangle.fromRectangle(pageFormat);
              pageFormat.width = 827;
            } else if (pageFormat.height == 826) {
              pageFormat = mxRectangle.fromRectangle(pageFormat);
              pageFormat.height = 827;
            }
          } else if (f.key == 'a5') {
            if (pageFormat.width == 584) {
              pageFormat = mxRectangle.fromRectangle(pageFormat);
              pageFormat.width = 583;
            } else if (pageFormat.height == 584) {
              pageFormat = mxRectangle.fromRectangle(pageFormat);
              pageFormat.height = 583;
            }
          }

          if (pageFormat.width == f.format.width && pageFormat.height == f.format.height) {
            paperSizeSelect.value = f.key;
            portraitCheckBox.setAttribute('checked', 'checked');
            portraitCheckBox.defaultChecked = true;
            portraitCheckBox.checked = true;
            landscapeCheckBox.removeAttribute('checked');
            landscapeCheckBox.defaultChecked = false;
            landscapeCheckBox.checked = false;
            detected = true;
          } else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width) {
            paperSizeSelect.value = f.key;
            portraitCheckBox.removeAttribute('checked');
            portraitCheckBox.defaultChecked = false;
            portraitCheckBox.checked = false;
            landscapeCheckBox.setAttribute('checked', 'checked');
            landscapeCheckBox.defaultChecked = true;
            landscapeCheckBox.checked = true;
            detected = true;
          }
        }
      }

      // Selects custom format which is last in list
      if (!detected) {
        widthInput.value = pageFormat.width / 100;
        heightInput.value = pageFormat.height / 100;
        portraitCheckBox.setAttribute('checked', 'checked');
        paperSizeSelect.value = 'custom';
        formatDiv.style.display = 'none';
        customDiv.style.display = '';
      } else {
        formatDiv.style.display = '';
        customDiv.style.display = 'none';
      }
    }
  };

  listener();
  div.appendChild(paperSizeSelect);
  mxUtils.br(div);

  div.appendChild(formatDiv);
  div.appendChild(customDiv);

  var currentPageFormat = pageFormat;

  var update = function (evt, selectChanged) {
    var f = pf[paperSizeSelect.value];

    if (f.format != null) {
      widthInput.value = f.format.width / 100;
      heightInput.value = f.format.height / 100;
      customDiv.style.display = 'none';
      formatDiv.style.display = '';
    } else {
      formatDiv.style.display = 'none';
      customDiv.style.display = '';
    }

    var wi = parseFloat(widthInput.value);

    if (isNaN(wi) || wi <= 0) {
      widthInput.value = pageFormat.width / 100;
    }

    var hi = parseFloat(heightInput.value);

    if (isNaN(hi) || hi <= 0) {
      heightInput.value = pageFormat.height / 100;
    }

    var newPageFormat = new mxRectangle(0, 0,
      Math.floor(parseFloat(widthInput.value) * 100),
      Math.floor(parseFloat(heightInput.value) * 100));

    if (paperSizeSelect.value != 'custom' && landscapeCheckBox.checked) {
      newPageFormat = new mxRectangle(0, 0, newPageFormat.height, newPageFormat.width);
    }

    // Initial select of custom should not update page format to avoid update of combo
    if ((!selectChanged || !customSize) && (newPageFormat.width != currentPageFormat.width ||
        newPageFormat.height != currentPageFormat.height)) {
      currentPageFormat = newPageFormat;

      // Updates page format and reloads format panel
      if (pageFormatListener != null) {
        pageFormatListener(currentPageFormat);
      }
    }
  };

  mxEvent.addListener(portraitSpan, 'click', function (evt) {
    portraitCheckBox.checked = true;
    update(evt);
    mxEvent.consume(evt);
  });

  mxEvent.addListener(landscapeSpan, 'click', function (evt) {
    landscapeCheckBox.checked = true;
    update(evt);
    mxEvent.consume(evt);
  });

  mxEvent.addListener(widthInput, 'blur', update);
  mxEvent.addListener(widthInput, 'click', update);
  mxEvent.addListener(heightInput, 'blur', update);
  mxEvent.addListener(heightInput, 'click', update);
  mxEvent.addListener(landscapeCheckBox, 'change', update);
  mxEvent.addListener(portraitCheckBox, 'change', update);
  mxEvent.addListener(paperSizeSelect, 'change', function (evt) {
    // Handles special case where custom was chosen
    customSize = paperSizeSelect.value == 'custom';
    update(evt, true);
  });

  update();
  return {
    set: function (value) {
      pageFormat = value;
      listener(null, null, true);
    },
    get: function () {
      return currentPageFormat;
    },
    widthInput: widthInput,
    heightInput: heightInput
  };
};

/**
 * 
 */
//页面尺寸布局
PageSetupDialog.getFormats = function () {
  return [{
      key: 'letter',
      title: 'US-Letter (8,5" x 11")',
      format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT
    },
    {
      key: 'legal',
      title: 'US-Legal (8,5" x 14")',
      format: new mxRectangle(0, 0, 850, 1400)
    },
    {
      key: 'tabloid',
      title: 'US-Tabloid (11" x 17")',
      format: new mxRectangle(0, 0, 1100, 1700)
    },
    {
      key: 'executive',
      title: 'US-Executive (7" x 10")',
      format: new mxRectangle(0, 0, 700, 1000)
    },
    {
      key: 'a0',
      title: 'A0 (841 mm x 1189 mm)',
      format: new mxRectangle(0, 0, 3300, 4681)
    },
    {
      key: 'a1',
      title: 'A1 (594 mm x 841 mm)',
      format: new mxRectangle(0, 0, 2339, 3300)
    },
    {
      key: 'a2',
      title: 'A2 (420 mm x 594 mm)',
      format: new mxRectangle(0, 0, 1654, 2336)
    },
    {
      key: 'a3',
      title: 'A3 (297 mm x 420 mm)',
      format: new mxRectangle(0, 0, 1169, 1654)
    },
    {
      key: 'a4',
      title: 'A4 (210 mm x 297 mm)',
      format: mxConstants.PAGE_FORMAT_A4_PORTRAIT
    },
    {
      key: 'a5',
      title: 'A5 (148 mm x 210 mm)',
      format: new mxRectangle(0, 0, 583, 827)
    },
    {
      key: 'a6',
      title: 'A6 (105 mm x 148 mm)',
      format: new mxRectangle(0, 0, 413, 583)
    },
    {
      key: 'a7',
      title: 'A7 (74 mm x 105 mm)',
      format: new mxRectangle(0, 0, 291, 413)
    },
    {
      key: 'b4',
      title: 'B4 (250 mm x 353 mm)',
      format: new mxRectangle(0, 0, 980, 1390)
    },
    {
      key: 'b5',
      title: 'B5 (176 mm x 250 mm)',
      format: new mxRectangle(0, 0, 690, 980)
    },
    {
      key: '16-9',
      title: '16:9 (1600 x 900)',
      format: new mxRectangle(0, 0, 900, 1600)
    },
    {
      key: '16-10',
      title: '16:10 (1920 x 1200)',
      format: new mxRectangle(0, 0, 1200, 1920)
    },
    {
      key: '4-3',
      title: '4:3 (1600 x 1200)',
      format: new mxRectangle(0, 0, 1200, 1600)
    },
    {
      key: 'custom',
      title: mxResources.get('custom'),
      format: null
    }
  ];
};

/**
 * Constructs a new filename dialog.
 */
var FilenameDialog = function (editorUi, filename, buttonText, fn, label, validateFn, content, helpLink, closeOnBtn, cancelFn, hints, w) {
	closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true;
	var row, td;

	var table = document.createElement('table');
	var tbody = document.createElement('tbody');
	table.style.marginTop = '8px';

	row = document.createElement('tr');

	td = document.createElement('td');
	td.style.whiteSpace = 'nowrap';
	td.style.fontSize = '10pt';
	td.style.width = (hints) ? '80px' : '120px';
	mxUtils.write(td, (label || mxResources.get('filename')) + ':');

	row.appendChild(td);

	var nameInput = document.createElement('input');
	nameInput.setAttribute('value', filename || '');
	nameInput.style.marginLeft = '4px';
	nameInput.style.width = (w != null) ? w + 'px' : '180px';

	var genericBtn = mxUtils.button(buttonText, function () {
		if (validateFn == null || validateFn(nameInput.value)) {
			if (closeOnBtn) {
				editorUi.hideDialog();
			}

			fn(nameInput.value);
		}
	});
	genericBtn.className = 'geBtn gePrimaryBtn';

	this.init = function () {
		if (label == null && content != null) {
			return;
		}

		nameInput.focus();

		if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) {
			nameInput.select();
		} else {
			document.execCommand('selectAll', false, null);
		}

		// Installs drag and drop handler for links
		if (Graph.fileSupport) {
			// Setup the dnd listeners
			var dlg = table.parentNode;

			if (dlg != null) {
				var graph = editorUi.editor.graph;
				var dropElt = null;

				mxEvent.addListener(dlg, 'dragleave', function (evt) {
					if (dropElt != null) {
						dropElt.style.backgroundColor = '';
						dropElt = null;
					}

					evt.stopPropagation();
					evt.preventDefault();
				});

				mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function (evt) {
					// IE 10 does not implement pointer-events so it can't have a drop highlight
					if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10)) {
						dropElt = nameInput;
						dropElt.style.backgroundColor = '#ebf2f9';
					}

					evt.stopPropagation();
					evt.preventDefault();
				}));

				mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function (evt) {
					if (dropElt != null) {
						dropElt.style.backgroundColor = '';
						dropElt = null;
					}

					if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) {
						nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list'));
						genericBtn.click();
					}

					evt.stopPropagation();
					evt.preventDefault();
				}));
			}
		}
	};

	td = document.createElement('td');
	td.style.whiteSpace = 'nowrap';
	td.appendChild(nameInput);
	row.appendChild(td);

	if (label != null || content == null) {
		tbody.appendChild(row);

		if (hints != null) {
			if (editorUi.editor.diagramFileTypes != null) {
				var typeSelect = FilenameDialog.createFileTypes(editorUi, nameInput, editorUi.editor.diagramFileTypes);
				typeSelect.style.marginLeft = '6px';
				typeSelect.style.width = '74px';

				td.appendChild(typeSelect);
				nameInput.style.width = (w != null) ? (w - 40) + 'px' : '140px';
			}

			td.appendChild(FilenameDialog.createTypeHint(editorUi, nameInput, hints));
		}
	}

	if (content != null) {
		row = document.createElement('tr');
		td = document.createElement('td');
		td.colSpan = 2;
		td.appendChild(content);
		row.appendChild(td);
		tbody.appendChild(row);
	}

	row = document.createElement('tr');
	td = document.createElement('td');
	td.colSpan = 2;
	td.style.paddingTop = '20px';
	td.style.whiteSpace = 'nowrap';
	td.setAttribute('align', 'right');

	var cancelBtn = mxUtils.button(mxResources.get('cancel'), function () {
		editorUi.hideDialog();

		if (cancelFn != null) {
			cancelFn();
		}
	});
	cancelBtn.className = 'geBtn';

	if (editorUi.editor.cancelFirst) {
		td.appendChild(cancelBtn);
	}

	if (helpLink != null) {
		var helpBtn = mxUtils.button(mxResources.get('help'), function () {
			editorUi.editor.graph.openLink(helpLink);
		});

		helpBtn.className = 'geBtn';
		td.appendChild(helpBtn);
	}

	mxEvent.addListener(nameInput, 'keypress', function (e) {
		if (e.keyCode == 13) {
			genericBtn.click();
		}
	});

	td.appendChild(genericBtn);

	if (!editorUi.editor.cancelFirst) {
		td.appendChild(cancelBtn);
	}

	row.appendChild(td);
	tbody.appendChild(row);
	table.appendChild(tbody);

	this.container = table;
};

/**
 * 
 */
FilenameDialog.filenameHelpLink = null;

/**
 * 
 */
FilenameDialog.createTypeHint = function (ui, nameInput, hints) {
	var hint = document.createElement('img');
	hint.style.cssText = 'vertical-align:top;height:16px;width:16px;margin-left:4px;background-repeat:no-repeat;background-position:center bottom;cursor:pointer;';
	mxUtils.setOpacity(hint, 70);

	var nameChanged = function () {
		hint.setAttribute('src', Editor.helpImage);
		hint.setAttribute('title', mxResources.get('help'));

		for (var i = 0; i < hints.length; i++) {
			if (hints[i].ext.length > 0 && nameInput.value.toLowerCase().substring(
					nameInput.value.length - hints[i].ext.length - 1) == '.' + hints[i].ext) {
				hint.setAttribute('src', mxClient.imageBasePath + '/warning.png');
				hint.setAttribute('title', mxResources.get(hints[i].title));
				break;
			}
		}
	};

	mxEvent.addListener(nameInput, 'keyup', nameChanged);
	mxEvent.addListener(nameInput, 'change', nameChanged);
	mxEvent.addListener(hint, 'click', function (evt) {
		var title = hint.getAttribute('title');

		if (hint.getAttribute('src') == Editor.helpImage) {
			ui.editor.graph.openLink(FilenameDialog.filenameHelpLink);
		} else if (title != '') {
			ui.showError(null, title, mxResources.get('help'), function () {
				ui.editor.graph.openLink(FilenameDialog.filenameHelpLink);
			}, null, mxResources.get('ok'), null, null, null, 340, 90);
		}

		mxEvent.consume(evt);
	});

	nameChanged();

	return hint;
};

/**
 * 
 */
FilenameDialog.createFileTypes = function (editorUi, nameInput, types) {
	var typeSelect = document.createElement('select');

	for (var i = 0; i < types.length; i++) {
		var typeOption = document.createElement('option');
		typeOption.setAttribute('value', i);
		mxUtils.write(typeOption, mxResources.get(types[i].description) +
			' (.' + types[i].extension + ')');
		typeSelect.appendChild(typeOption);
	}

	mxEvent.addListener(typeSelect, 'change', function (evt) {
		var ext = types[typeSelect.value].extension;
		var idx = nameInput.value.lastIndexOf('.');

		if (idx > 0) {
			var ext = types[typeSelect.value].extension;
			nameInput.value = nameInput.value.substring(0, idx + 1) + ext;
		} else {
			nameInput.value = nameInput.value + '.' + ext;
		}

		if ('createEvent' in document) {
			var changeEvent = document.createEvent('HTMLEvents');
			changeEvent.initEvent('change', false, true);
			nameInput.dispatchEvent(changeEvent);
		} else {
			nameInput.fireEvent('onchange');
		}
	});

	var nameInputChanged = function (evt) {
		var idx = nameInput.value.lastIndexOf('.');
		var active = 0;

		// Finds current extension
		if (idx > 0) {
			var ext = nameInput.value.toLowerCase().substring(idx + 1);

			for (var i = 0; i < types.length; i++) {
				if (ext == types[i].extension) {
					active = i;
					break;
				}
			}
		}

		typeSelect.value = active;
	};

	mxEvent.addListener(nameInput, 'change', nameInputChanged);
	mxEvent.addListener(nameInput, 'keyup', nameInputChanged);
	nameInputChanged();

	return typeSelect;
};


/**
 * Disables client-side image export if math is enabled.
 */
Editor.prototype.isExportToCanvas = function () {
  return mxClient.IS_CHROMEAPP || this.useCanvasForExport;
};

/**
 * Default length for global unique IDs.
 */
Editor.prototype.guid = function (length) {
  var len = (length != null) ? length : Editor.GUID_LENGTH;
  var rtn = [];
  for (var i = 0; i < len; i++) {
    rtn.push(Editor.GUID_ALPHABET.charAt(Math.floor(Math.random() * Editor.GUID_ALPHABET.length)));
  }
  return rtn.join('');
};

/**
 *
 */
Editor.prototype.exportToCanvas = function (callback, width, imageCache, background, error, limitHeight,
  ignoreSelection, scale, transparentBackground, addShadow, converter, graph, border, noCrop, grid,
  keepTheme) {
  try {
    limitHeight = (limitHeight != null) ? limitHeight : true;
    ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
    graph = (graph != null) ? graph : this.graph;
    border = (border != null) ? border : 0;

    var bg = (transparentBackground) ? null : graph.background;

    if (bg == mxConstants.NONE) {
      bg = null;
    }

    if (bg == null) {
      bg = background;
    }

    // Handles special case where background is null but transparent is false
    if (bg == null && transparentBackground == false) {
      bg = (keepTheme) ? this.graph.defaultPageBackgroundColor : '#ffffff';;
    }

    this.convertImages(graph.getSvg(null, null, null, noCrop, null, ignoreSelection,
      null, null, null, addShadow, null, keepTheme), mxUtils.bind(this, function (svgRoot) {
      try {
        var img = new Image();

        img.onload = mxUtils.bind(this, function () {
          try {
            var canvas = document.createElement('canvas');
            var w = parseInt(svgRoot.getAttribute('width'));
            var h = parseInt(svgRoot.getAttribute('height'));
            scale = (scale != null) ? scale : 1;

            if (width != null) {
              scale = (!limitHeight) ? width / w : Math.min(1, Math.min((width * 3) / (h * 4), width / w));
            }

            w = Math.ceil(scale * w) + 2 * border;
            h = Math.ceil(scale * h) + 2 * border;

            canvas.setAttribute('width', w);
            canvas.setAttribute('height', h);
            var ctx = canvas.getContext('2d');

            if (bg != null) {
              ctx.beginPath();
              ctx.rect(0, 0, w, h);
              ctx.fillStyle = bg;
              ctx.fill();
            }

            ctx.scale(scale, scale);

            function drawImage() {
              // Workaround for broken data URI images in Safari on first export
              if (mxClient.IS_SF) {
                window.setTimeout(function () {
                  ctx.drawImage(img, border / scale, border / scale);
                  callback(canvas);
                }, 0);
              } else {
                ctx.drawImage(img, border / scale, border / scale);
                callback(canvas);
              }
            };

            if (grid) {
              var view = graph.view;
              var curViewScale = view.scale;
              view.scale = 1; //Reset the scale temporary to generate unscaled grid image which is then scaled
              var gridImage = btoa(unescape(encodeURIComponent(view.createSvgGrid(view.gridColor))));
              view.scale = curViewScale;
              gridImage = 'data:image/svg+xml;base64,' + gridImage;
              var phase = graph.gridSize * view.gridSteps * scale;

              var b = graph.getGraphBounds();
              var tx = view.translate.x * curViewScale;
              var ty = view.translate.y * curViewScale;
              var x0 = tx + (b.x - tx) / curViewScale;
              var y0 = ty + (b.y - ty) / curViewScale;

              var background = new Image();

              background.onload = function () {
                try {
                  var x = -Math.round(phase - mxUtils.mod((tx - x0) * scale, phase));
                  var y = -Math.round(phase - mxUtils.mod((ty - y0) * scale, phase));

                  for (var i = x; i < w; i += phase) {
                    for (var j = y; j < h; j += phase) {
                      ctx.drawImage(background, i / scale, j / scale);
                    }
                  }
                  drawImage();
                } catch (e) {
                  if (error != null) {
                    error(e);
                  }
                }
              };

              background.onerror = function (e) {
                if (error != null) {
                  error(e);
                }
              };

              background.src = gridImage;
            } else {
              drawImage();
            }
          } catch (e) {
            if (error != null) {
              error(e);
            }
          }
        });

        img.onerror = function (e) {
          //console.log('img', e, img.src);
          if (error != null) {
            error(e);
          }
        };

        if (addShadow) {
          this.graph.addSvgShadow(svgRoot);
        }

        if (this.graph.mathEnabled) {
          this.addMathCss(svgRoot);
        }

        var done = mxUtils.bind(this, function () {
          try {
            if (this.resolvedFontCss != null) {
              this.addFontCss(svgRoot, this.resolvedFontCss);
            }
            img.src = this.createSvgDataUri(mxUtils.getXml(svgRoot));
          } catch (e) {
            if (error != null) {
              error(e);
            }
          }
        });

        this.embedExtFonts(mxUtils.bind(this, function (extFontsEmbeddedCss) {
          try {
            if (extFontsEmbeddedCss != null) {
              this.addFontCss(svgRoot, extFontsEmbeddedCss);
            }
            this.loadFonts(done);
          } catch (e) {
            if (error != null) {
              error(e);
            }
          }
        }));
      } catch (e) {
        //console.log('src', e, img.src);
        if (error != null) {
          error(e);
        }
      }
    }), imageCache, converter);
  } catch (e) {
    if (error != null) {
      error(e);
    }
  }
};

/**
 * Adds the given text to the compressed or non-compressed text chunk.
 */
Editor.prototype.writeGraphModelToPng = function (data, type, key, value, error) {
  var base64 = data.substring(data.indexOf(',') + 1);
  var f = (window.atob) ? atob(base64) : Base64.decode(base64, true);
  var pos = 0;

  function fread(d, count) {
    var start = pos;
    pos += count;

    return d.substring(start, pos);
  };

  // Reads unsigned long 32 bit big endian
  function _freadint(d) {
    var bytes = fread(d, 4);

    return bytes.charCodeAt(3) + (bytes.charCodeAt(2) << 8) +
      (bytes.charCodeAt(1) << 16) + (bytes.charCodeAt(0) << 24);
  };

  function writeInt(num) {
    return String.fromCharCode((num >> 24) & 0x000000ff, (num >> 16) & 0x000000ff,
      (num >> 8) & 0x000000ff, num & 0x000000ff);
  };

  // Checks signature
  if (fread(f, 8) != String.fromCharCode(137) + 'PNG' + String.fromCharCode(13, 10, 26, 10)) {
    if (error != null) {
      error();
    }
    return;
  }

  // Reads header chunk
  fread(f, 4);

  if (fread(f, 4) != 'IHDR') {
    if (error != null) {
      error();
    }
    return;
  }

  fread(f, 17);
  var result = f.substring(0, pos);

  do {
    var n = _freadint(f);
    var chunk = fread(f, 4);

    if (chunk == 'IDAT') {
      result = f.substring(0, pos - 8);

      if (type == 'pHYs' && key == 'dpi') {
        var dpm = Math.round(value / 0.0254); //One inch is equal to exactly 0.0254 meters.
        var chunkData = writeInt(dpm) + writeInt(dpm) + String.fromCharCode(1);
      } else {
        var chunkData = key + String.fromCharCode(0) +
          ((type == 'zTXt') ? String.fromCharCode(0) : '') +
          value;
      }

      var crc = 0xffffffff;
      crc = Editor.updateCRC(crc, type, 0, 4);
      crc = Editor.updateCRC(crc, chunkData, 0, chunkData.length);

      result += writeInt(chunkData.length) + type + chunkData + writeInt(crc ^ 0xffffffff);
      result += f.substring(pos - 8, f.length);
      break;
    }

    result += f.substring(pos - 8, pos - 4 + n);
    fread(f, n);
    fread(f, 4);
  }
  while (n);
  return 'data:image/png;base64,' + ((window.btoa) ? btoa(result) : Base64.encode(result, true));
};

Editor.crcTable = [];

for (var n = 0; n < 256; n++) {
  var c = n;

  for (var k = 0; k < 8; k++) {
    if ((c & 1) == 1) {
      c = 0xedb88320 ^ (c >>> 1);
    } else {
      c >>>= 1;
    }
    Editor.crcTable[n] = c;
  }
}

Editor.updateCRC = function (crc, data, off, len) {
  var c = crc;
  for (var n = 0; n < len; n++) {
    c = Editor.crcTable[(c ^ data.charCodeAt(off + n)) & 0xff] ^ (c >>> 8);
  }
  return c;
};

/**
 * 
 */
Editor.prototype.createSvgDataUri = function (svg) {
  return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
};

/**
 * For the fontCSS to be applied when rendering images on canvas, the actual
 * font data must be made available via a data URI encoding of the file.
 */
Editor.prototype.loadFonts = function (then) {
  if (this.fontCss != null && this.resolvedFontCss == null) {
    this.embedCssFonts(this.fontCss, mxUtils.bind(this, function (resolvedFontCss) {
      this.resolvedFontCss = resolvedFontCss;
      then();
    }));
  } else {
    then();
  }
};

/**
 * Embeds external fonts
 */
Editor.prototype.embedExtFonts = function (callback) {
  var extFonts = this.graph.extFonts;
  if (extFonts != null && extFonts.length > 0) {
    var styleCnt = '',
      waiting = 0;

    if (this.cachedGoogleFonts == null) {
      this.cachedGoogleFonts = {};
    }
    var googleCssDone = mxUtils.bind(this, function () {
      if (waiting == 0) {
        this.embedCssFonts(styleCnt, callback);
      }
    });

    for (var i = 0; i < extFonts.length; i++) {
      (mxUtils.bind(this, function (fontName, fontUrl) {
        if (fontUrl.indexOf(Editor.GOOGLE_FONTS) == 0) {
          if (this.cachedGoogleFonts[fontUrl] == null) {
            waiting++;

            this.loadUrl(fontUrl, mxUtils.bind(this, function (css) {
              this.cachedGoogleFonts[fontUrl] = css;
              styleCnt += css;
              waiting--;
              googleCssDone();
            }), mxUtils.bind(this, function (err) {
              // LATER: handle error
              waiting--;
              styleCnt += '@import url(' + fontUrl + ');';
              googleCssDone();
            }));
          } else {
            styleCnt += this.cachedGoogleFonts[fontUrl];
          }
        } else {
          styleCnt += '@font-face {' +
            'font-family: "' + fontName + '";' +
            'src: url("' + fontUrl + '");' +
            '}';
        }
      }))(extFonts[i].name, extFonts[i].url);
    }
    googleCssDone();
  } else {
    callback();
  }
};


/**
 * Return array of string values, or NULL if CSV string not well formed.
 * 插入高级布局 CSV 方法
 */
Editor.prototype.csvToArray = function (text) {
  var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
  var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
  // Return NULL if input string is not well formed CSV string.
  if (!re_valid.test(text)) return null;
  var a = []; // Initialize array to receive values.
  text.replace(re_value, // "Walk" the string using replace with callback.
    function (m0, m1, m2, m3) {
      // Remove backslash from \' in single quoted values.
      if (m1 !== undefined) a.push(m1.replace(/\\'/g, "'"));
      // Remove backslash from \" in double quoted values.
      else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"'));
      else if (m3 !== undefined) a.push(m3);
      return ''; // Return empty string.
    });
  // Handle special case of empty last value.
  if (/,\s*$/.test(text)) a.push('');
  return a;
};


/**
 * Converts all images in the SVG output to data URIs for immediate rendering
 */
Editor.prototype.convertImages = function (svgRoot, callback, imageCache, converter) {
  // Converts images to data URLs for immediate painting
  if (converter == null) {
    converter = this.createImageUrlConverter();
  }

  // Barrier for asynchronous image loading
  var counter = 0;

  function inc() {
    counter++;
  };

  function dec() {
    counter--;

    if (counter == 0) {
      callback(svgRoot);
    }
  };

  var cache = imageCache || new Object();

  var convertImages = mxUtils.bind(this, function (tagName, srcAttr) {
    var images = svgRoot.getElementsByTagName(tagName);

    for (var i = 0; i < images.length; i++) {
      (mxUtils.bind(this, function (img) {
        try {
          if (img != null) {
            var src = converter.convert(img.getAttribute(srcAttr));

            // Data URIs are pass-through
            if (src != null && src.substring(0, 5) != 'data:') {
              var tmp = cache[src];

              if (tmp == null) {
                inc();

                this.convertImageToDataUri(src, function (uri) {
                  if (uri != null) {
                    cache[src] = uri;
                    img.setAttribute(srcAttr, uri);
                  }

                  dec();
                });
              } else {
                img.setAttribute(srcAttr, tmp);
              }
            } else if (src != null) {
              img.setAttribute(srcAttr, src);
            }
          }
        } catch (e) {
          // ignore
        }
      }))(images[i]);
    }
  });

  // Converts all known image tags in output
  // LATER: Add support for images in CSS
  convertImages('image', 'xlink:href');
  convertImages('img', 'src');

  // All from cache or no images
  if (counter == 0) {
    callback(svgRoot);
  }
};

/**
 * Converts all images in the SVG output to data URIs for immediate rendering
 */
Editor.prototype.createImageUrlConverter = function () {
  var converter = new mxUrlConverter();
  converter.updateBaseUrl();

  // Extends convert to avoid CORS using an image proxy server where needed
  var convert = converter.convert;
  var self = this;

  converter.convert = function (src) {
    if (src != null) {
      var remote = src.substring(0, 7) == 'http://' || src.substring(0, 8) == 'https://';

      if (remote && !navigator.onLine) {
        src = Editor.svgBrokenImage.src;
      } else if (remote && src.substring(0, converter.baseUrl.length) != converter.baseUrl &&
        (!self.crossOriginImages || !self.isCorsEnabledForUrl(src))) {
        src = PROXY_URL + '?url=' + encodeURIComponent(src);
      } else if (src.substring(0, 19) != 'chrome-extension://' && !mxClient.IS_CHROMEAPP) {
        src = convert.apply(this, arguments);
      }
    }
    return src;
  };
  return converter;
};

/**
 * Extracts the XML from the compressed or non-compressed text chunk.
 */
Editor.extractGraphModelFromPng = function (data) {
  var result = null;
  try {
    var base64 = data.substring(data.indexOf(',') + 1);
    // Workaround for invalid character error in Safari
    var binary = (window.atob && !mxClient.IS_SF) ? atob(base64) : Base64.decode(base64, true);

    EditorUi.parsePng(binary, mxUtils.bind(this, function (pos, type, length) {
      var value = binary.substring(pos + 8, pos + 8 + length);
      if (type == 'zTXt') {
        var idx = value.indexOf(String.fromCharCode(0));

        if (value.substring(0, idx) == 'mxGraphModel') {

          // Workaround for Java URL Encoder using + for spaces, which isn't compatible with JS
          var xmlData = pako.inflateRaw(value.substring(idx + 2), {
            to: 'string'
          }).replace(/\+/g, ' ');

          if (xmlData != null && xmlData.length > 0) {
            result = xmlData;
          }
        }
      }
      // Uncompressed section is normally not used
      else if (type == 'tEXt') {
        var vals = value.split(String.fromCharCode(0));

        if (vals.length > 1 && (vals[0] == 'mxGraphModel' ||
            vals[0] == 'mxfile')) {
          result = vals[1];
        }
      }
      if (result != null || type == 'IDAT') {
        // Stops processing the file as our text chunks
        // are always placed before the data section
        return true;
      }
    }));
  } catch (e) {
    // ignores decoding errors
  }
  if (result != null && result.charAt(0) == '%') {
    result = decodeURIComponent(result);
  }
  // Workaround for double encoded content
  if (result != null && result.charAt(0) == '%') {
    result = decodeURIComponent(result);
  }
  return result;
};
/**
 * Helper function to extract the graph model XML node.
 */
Editor.prototype.extractGraphModel = function (node, allowMxFile, checked) {
  // console.log( "extractGraphModel:",node,allowMxFile,checked);

  //1.左侧库里面有内容并且是 svg 绘制的
  if (node != null && typeof (pako) !== 'undefined') {

    console.log("左侧库里面有内容1");

    var tmp = node.ownerDocument.getElementsByTagName('div');
    var divs = [];

    if (tmp != null && tmp.length > 0) {
      for (var i = 0; i < tmp.length; i++) {
        if (tmp[i].getAttribute('class') == 'mxgraph') {
          divs.push(tmp[i]);
          break;
        }
      }
    }

    if (divs.length > 0) {
      var data = divs[0].getAttribute('data-mxgraph');
      if (data != null) {
        var config = JSON.parse(data);
        if (config != null && config.xml != null) {
          var doc2 = mxUtils.parseXml(config.xml);
          node = doc2.documentElement;
        }
      } else {
        var divs2 = divs[0].getElementsByTagName('div');
        if (divs2.length > 0) {
          var data = mxUtils.getTextContent(divs2[0]);
          data = Graph.decompress(data, null, checked);
          if (data.length > 0) {
            var doc2 = mxUtils.parseXml(data);
            node = doc2.documentElement;
          }
        }
      }
    }
  }

  if (node != null && node.nodeName == 'svg') {
    // alert("222")
    var tmp = node.getAttribute('content');
    if (tmp != null && tmp.charAt(0) != '<' && tmp.charAt(0) != '%') {
      tmp = unescape((window.atob) ? atob(tmp) : Base64.decode(cont, tmp));
    }
    if (tmp != null && tmp.charAt(0) == '%') {
      tmp = decodeURIComponent(tmp);
    }
    if (tmp != null && tmp.length > 0) {
      node = mxUtils.parseXml(tmp).documentElement;
    } else {
      throw {
        message: mxResources.get('notADiagramFile')
      };
    }
  }

  if (node != null && !allowMxFile) {
    //导入的是svg 图
    // console.log( "node.nodeName:",node.nodeName);
    var diagramNode = null;
    if (node.nodeName == 'diagram') {
      diagramNode = node;
    } else if (node.nodeName == 'mxfile') {
      var diagrams = node.getElementsByTagName('diagram');
      if (diagrams.length > 0) {
        diagramNode = diagrams[Math.max(0, Math.min(diagrams.length - 1, urlParams['page'] || 0))];
      }
    }
    if (diagramNode != null) {
      node = Editor.parseDiagramNode(diagramNode, checked);
    }
  }

  if (node != null && node.nodeName != 'mxGraphModel' && (!allowMxFile || node.nodeName != 'mxfile')) {
    node = null;
  }
  return node;
};

/**
 * Extracts the XML from the compressed or non-compressed text chunk.
 */
Editor.prototype.parseDiagramNode = function (diagramNode, checked) {
  var text = mxUtils.trim(mxUtils.getTextContent(diagramNode));
  var node = null;
  if (text.length > 0) {
    var tmp = Graph.decompress(text, null, checked);
    if (tmp != null && tmp.length > 0) {
      node = mxUtils.parseXml(tmp).documentElement;
    }
  } else {
    var temp = mxUtils.getChildNodes(diagramNode);
    if (temp.length > 0) {
      // Creates new document for unique IDs within mxGraphModel
      var doc = mxUtils.createXmlDocument();
      doc.appendChild(doc.importNode(temp[0], true));
      node = doc.documentElement;
    }
  }
  return node;
};

/**
 * Extracts the XML from the compressed or non-compressed text chunk.
 */
Editor.getDiagramNodeXml = function (diagramNode) {
  var text = mxUtils.getTextContent(diagramNode);
  var xml = null;
  if (text.length > 0) {
    xml = Graph.decompress(text);
  } else if (diagramNode.firstChild != null) {
    xml = mxUtils.getXml(diagramNode.firstChild);
  }
  return xml;
};


/**
 * Static overrides
 */
(function () {
  // Uses HTML for background pages (to support grid background image)
  mxGraphView.prototype.validateBackgroundPage = function () {
    var graph = this.graph;

    if (graph.container != null && !graph.transparentBackground) {
      if (graph.pageVisible) {
        var bounds = this.getBackgroundPageBounds();

        if (this.backgroundPageShape == null) {
          // Finds first element in graph container
          var firstChild = graph.container.firstChild;

          while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT) {
            firstChild = firstChild.nextSibling;
          }

          if (firstChild != null) {
            this.backgroundPageShape = this.createBackgroundPageShape(bounds);
            this.backgroundPageShape.scale = 1;

            // Shadow filter causes problems in outline window in quirks mode. IE8 standards
            // also has known rendering issues inside mxWindow but not using shadow is worse.
            this.backgroundPageShape.isShadow = !mxClient.IS_QUIRKS;
            this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML;
            this.backgroundPageShape.init(graph.container);

            // Required for the browser to render the background page in correct order
            firstChild.style.position = 'absolute';
            graph.container.insertBefore(this.backgroundPageShape.node, firstChild);
            this.backgroundPageShape.redraw();

            this.backgroundPageShape.node.className = 'geBackgroundPage';

            // Adds listener for double click handling on background
            mxEvent.addListener(this.backgroundPageShape.node, 'dblclick',
              mxUtils.bind(this, function (evt) {
                graph.dblClick(evt);
              })
            );

            // Adds basic listeners for graph event dispatching outside of the
            // container and finishing the handling of a single gesture
            mxEvent.addGestureListeners(this.backgroundPageShape.node,
              mxUtils.bind(this, function (evt) {
                graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
              }),
              mxUtils.bind(this, function (evt) {
                // Hides the tooltip if mouse is outside container
                if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
                  graph.tooltipHandler.hide();
                }

                if (graph.isMouseDown && !mxEvent.isConsumed(evt)) {
                  graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
                }
              }),
              mxUtils.bind(this, function (evt) {
                graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
              })
            );
          }
        } else {
          this.backgroundPageShape.scale = 1;
          this.backgroundPageShape.bounds = bounds;
          this.backgroundPageShape.redraw();
        }
      } else if (this.backgroundPageShape != null) {
        this.backgroundPageShape.destroy();
        this.backgroundPageShape = null;
      }
      this.validateBackgroundStyles();
    }
  };

  // Updates the CSS of the background to draw the grid
  mxGraphView.prototype.validateBackgroundStyles = function () {
    var graph = this.graph;
    var color = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background;
    var gridColor = (color != null && this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff';
    var image = 'none';
    var position = '';

    if (graph.isGridEnabled()) {
      var phase = 10;

      if (mxClient.IS_SVG) {
        // Generates the SVG required for drawing the dynamic grid
        image = unescape(encodeURIComponent(this.createSvgGrid(gridColor)));
        image = (window.btoa) ? btoa(image) : Base64.encode(image, true);
        image = 'url(' + 'data:image/svg+xml;base64,' + image + ')'
        phase = graph.gridSize * this.scale * this.gridSteps;
      } else {
        // Fallback to grid wallpaper with fixed size
        image = 'url(' + this.gridImage + ')';
      }

      var x0 = 0;
      var y0 = 0;

      if (graph.view.backgroundPageShape != null) {
        var bds = this.getBackgroundPageBounds();

        x0 = 1 + bds.x;
        y0 = 1 + bds.y;
      }

      // Computes the offset to maintain origin for grid
      position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0, phase)) + 'px ' +
        -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0, phase)) + 'px';
    }

    var canvas = graph.view.canvas;
    if (canvas.ownerSVGElement != null) {
      canvas = canvas.ownerSVGElement;
    }

    if (graph.view.backgroundPageShape != null) {
      graph.view.backgroundPageShape.node.style.backgroundPosition = position;
      graph.view.backgroundPageShape.node.style.backgroundImage = image;
      graph.view.backgroundPageShape.node.style.backgroundColor = color;
      graph.container.className = 'geDiagramContainer geDiagramBackdrop';
      canvas.style.backgroundImage = 'none';
      //画布的默认背景颜色
      canvas.style.backgroundColor = '#01122E'; //'';
    } else {
      graph.container.className = 'geDiagramContainer';
      canvas.style.backgroundPosition = position;
      // 画布的背景颜色赋值
      canvas.style.backgroundColor =  '#01122E'; //color;
      //画布的背景图片不要显示了
      // canvas.style.backgroundImage = image;
    }
  };

  // Returns the SVG required for painting the background grid.
  mxGraphView.prototype.createSvgGrid = function (color) {
    var tmp = this.graph.gridSize * this.scale;
    while (tmp < this.minGridSize) {
      tmp *= 2;
    }

    var tmp2 = this.gridSteps * tmp;
    // Small grid lines
    var d = [];

    for (var i = 1; i < this.gridSteps; i++) {
      var tmp3 = i * tmp;
      d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2);
    }

    // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround
    // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams.
    var size = tmp2;
    var svg = '<svg width="' + size + '" height="' + size + '" xmlns="' + mxConstants.NS_SVG + '">' +
      '<defs><pattern id="grid" width="' + tmp2 + '" height="' + tmp2 + '" patternUnits="userSpaceOnUse">' +
      '<path d="' + d.join(' ') + '" fill="none" stroke="' + color + '" opacity="0.2" stroke-width="1"/>' +
      '<path d="M ' + tmp2 + ' 0 L 0 0 0 ' + tmp2 + '" fill="none" stroke="' + color + '" stroke-width="1"/>' +
      '</pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg>';

    return svg;
  };

  // Adds panning for the grid with no page view and disabled scrollbars
  var mxGraphPanGraph = mxGraph.prototype.panGraph;
  mxGraph.prototype.panGraph = function (dx, dy) {
    mxGraphPanGraph.apply(this, arguments);

    if (this.shiftPreview1 != null) {
      var canvas = this.view.canvas;

      if (canvas.ownerSVGElement != null) {
        canvas = canvas.ownerSVGElement;
      }

      var phase = this.gridSize * this.view.scale * this.view.gridSteps;
      var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' +
        -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px';
      canvas.style.backgroundPosition = position;
    }
  };

  // Draws page breaks only within the page
  mxGraph.prototype.updatePageBreaks = function (visible, width, height) {
    var scale = this.view.scale;
    var tr = this.view.translate;
    var fmt = this.pageFormat;
    var ps = scale * this.pageScale;

    var bounds2 = this.view.getBackgroundPageBounds();
    width = bounds2.width;
    height = bounds2.height;
    var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps);

    // Does not show page breaks if the scale is too small
    visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;

    var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0;
    var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0;
    var right = bounds2.x + width;
    var bottom = bounds2.y + height;

    if (this.horizontalPageBreaks == null && horizontalCount > 0) {
      this.horizontalPageBreaks = [];
    }

    if (this.verticalPageBreaks == null && verticalCount > 0) {
      this.verticalPageBreaks = [];
    }

    var drawPageBreaks = mxUtils.bind(this, function (breaks) {
      if (breaks != null) {
        var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount;

        for (var i = 0; i <= count; i++) {
          var pts = (breaks == this.horizontalPageBreaks) ? [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)),
            new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))
          ] : [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)),
            new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))
          ];

          if (breaks[i] != null) {
            breaks[i].points = pts;
            breaks[i].redraw();
          } else {
            var pageBreak = new mxPolyline(pts, this.pageBreakColor);
            pageBreak.dialect = this.dialect;
            pageBreak.isDashed = this.pageBreakDashed;
            pageBreak.pointerEvents = false;
            pageBreak.init(this.view.backgroundPane);
            pageBreak.redraw();
            breaks[i] = pageBreak;
          }
        }
        for (var i = count; i < breaks.length; i++) {
          breaks[i].destroy();
        }
        breaks.splice(count, breaks.length - count);
      }
    });

    drawPageBreaks(this.horizontalPageBreaks);
    drawPageBreaks(this.verticalPageBreaks);
  };

	// Disables removing relative children and table rows and cells from parents
	var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent;
	mxGraphHandler.prototype.shouldRemoveCellsFromParent = function (parent, cells, evt) {
		for (var i = 0; i < cells.length; i++) {
			if (this.graph.isTableCell(cells[i]) || this.graph.isTableRow(cells[i])) {
				return false;
			} else if (this.graph.getModel().isVertex(cells[i])) {
				var geo = this.graph.getCellGeometry(cells[i]);

        if (geo != null && geo.relative) {
          return false;
        }
      }
    }

    return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments);
  };

  // Overrides to ignore hotspot only for target terminal
  var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker;
  mxConnectionHandler.prototype.createMarker = function () {
    var marker = mxConnectionHandlerCreateMarker.apply(this, arguments);

    marker.intersects = mxUtils.bind(this, function (state, evt) {
      if (this.isConnecting()) {
        return true;
      }

      return mxCellMarker.prototype.intersects.apply(marker, arguments);
    });

    return marker;
  };

  // Creates background page shape
  mxGraphView.prototype.createBackgroundPageShape = function (bounds) {
    return new mxRectangleShape(bounds, '#ffffff', this.graph.defaultPageBorderColor);
  };

  // Fits the number of background pages to the graph
  mxGraphView.prototype.getBackgroundPageBounds = function () {
    var gb = this.getGraphBounds();

    // Computes unscaled, untranslated graph bounds
    var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0;
    var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0;
    var w = gb.width / this.scale;
    var h = gb.height / this.scale;

    var fmt = this.graph.pageFormat;
    var ps = this.graph.pageScale;

    var pw = fmt.width * ps;
    var ph = fmt.height * ps;

    var x0 = Math.floor(Math.min(0, x) / pw);
    var y0 = Math.floor(Math.min(0, y) / ph);
    var xe = Math.ceil(Math.max(1, x + w) / pw);
    var ye = Math.ceil(Math.max(1, y + h) / ph);

    var rows = xe - x0;
    var cols = ye - y0;

    var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale *
      (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph);

    return bounds;
  };

  // Add panning for background page in VML
  var graphPanGraph = mxGraph.prototype.panGraph;
  mxGraph.prototype.panGraph = function (dx, dy) {
    graphPanGraph.apply(this, arguments);

    if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) &&
      (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container))) {
      this.view.backgroundPageShape.node.style.marginLeft = dx + 'px';
      this.view.backgroundPageShape.node.style.marginTop = dy + 'px';
    }
  };

  /**
   * Consumes click events for disabled menu items.
   */
  var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem;
  mxPopupMenu.prototype.addItem = function (title, image, funct, parent, iconCls, enabled) {
    var result = mxPopupMenuAddItem.apply(this, arguments);

    if (enabled != null && !enabled) {
      mxEvent.addListener(result, 'mousedown', function (evt) {
        mxEvent.consume(evt);
      });
    }

    return result;
  };


	/**
	 * Selects tables before cells and rows.
	 */
	var mxGraphHandlerIsPropagateSelectionCell = mxGraphHandler.prototype.isPropagateSelectionCell;
	mxGraphHandler.prototype.isPropagateSelectionCell = function (cell, immediate, me) {
		var result = false;
		var parent = this.graph.model.getParent(cell)

		if (immediate) {
			var geo = (this.graph.model.isEdge(cell)) ? null :
				this.graph.getCellGeometry(cell);

			result = !this.graph.model.isEdge(parent) &&
				!this.graph.isSiblingSelected(cell) &&
				((geo != null && geo.relative) ||
					!this.graph.isContainer(parent) ||
					this.graph.isPart(cell));
		} else {
			result = mxGraphHandlerIsPropagateSelectionCell.apply(this, arguments);

			if (this.graph.isTableCell(cell) || this.graph.isTableRow(cell)) {
				var table = parent;

				if (!this.graph.isTable(table)) {
					table = this.graph.model.getParent(table);
				}

				result = !this.graph.selectionCellsHandler.isHandled(table) ||
					(this.graph.isCellSelected(table) && this.graph.isToggleEvent(me.getEvent())) ||
					(this.graph.isCellSelected(cell) && !this.graph.isToggleEvent(me.getEvent())) ||
					(this.graph.isTableCell(cell) && this.graph.isCellSelected(parent));
			}
		}

		return result;
	};


	/**
	 * Returns last selected ancestor
	 */
	mxPopupMenuHandler.prototype.getCellForPopupEvent = function (me) {
		var cell = me.getCell();
		var model = this.graph.getModel();
		var parent = model.getParent(cell);
		var state = this.graph.view.getState(parent);
		var selected = this.graph.isCellSelected(cell);

		while (state != null && (model.isVertex(parent) || model.isEdge(parent))) {
			var temp = this.graph.isCellSelected(parent);
			selected = selected || temp;

			if (temp || (!selected && (this.graph.isTableCell(cell) ||
					this.graph.isTableRow(cell)))) {
				cell = parent;
			}

			parent = model.getParent(parent);
		}

		return cell;
	};


  // Selects ancestors before descendants
  var graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent;
  mxGraphHandler.prototype.getInitialCellForEvent = function (me) {
    var model = this.graph.getModel();
    var psel = model.getParent(this.graph.getSelectionCell());
    var cell = graphHandlerGetInitialCellForEvent.apply(this, arguments);
    var parent = model.getParent(cell);

    if (psel == null || (psel != cell && psel != parent)) {
      while (!this.graph.isCellSelected(cell) && !this.graph.isCellSelected(parent) &&
        model.isVertex(parent) && !this.graph.isContainer(parent)) {
        cell = parent;
        parent = this.graph.getModel().getParent(cell);
      }
    }
    return cell;
  };

  // Selection is delayed to mouseup if ancestor is selected
  var graphHandlerIsDelayedSelection = mxGraphHandler.prototype.isDelayedSelection;
  mxGraphHandler.prototype.isDelayedSelection = function (cell, me) {
    var result = graphHandlerIsDelayedSelection.apply(this, arguments);

    if (!result) {
      var model = this.graph.getModel();
      var parent = model.getParent(cell);
      while (parent != null) {
        // Inconsistency for unselected parent swimlane is intended for easier moving
        // of stack layouts where the container title section is too far away
        if (this.graph.isCellSelected(parent) && model.isVertex(parent)) {
          result = true;
          break;
        }
        parent = model.getParent(parent);
      }
    }
    return result;
  };

  // Delayed selection of parent group
  mxGraphHandler.prototype.selectDelayed = function (me) {
    if (!this.graph.popupMenuHandler.isPopupTrigger(me)) {
      var cell = me.getCell();
      if (cell == null) {
        cell = this.cell;
      }

      // Selects folded cell for hit on folding icon
      var state = this.graph.view.getState(cell)
      if (state != null && me.isSource(state.control)) {
        this.graph.selectCellForEvent(cell, me.getEvent());
      } else {
        var model = this.graph.getModel();
        var parent = model.getParent(cell);
        while (!this.graph.isCellSelected(parent) && model.isVertex(parent)) {
          cell = parent;
          parent = model.getParent(cell);
        }
        this.graph.selectCellForEvent(cell, me.getEvent());
      }
    }
  };

})();

export default Editor;
export {
  OpenFile,
  Dialog,
  ErrorDialog,
  PrintDialog,
  PageSetupDialog
}